Merge "Only allow mirroring on a VirtualDisplay with a mirroring flag or MediaProjection instance." into udc-dev
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 ab0a8ad..55e6815 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -489,14 +489,6 @@
             | PackageManager.MATCH_DIRECT_BOOT_AWARE
             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
-    /**
-     * Whether we should allow apps into the
-     * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
-     * If false, any attempts to put an app into the bucket will put the app into the
-     * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RARE} bucket instead.
-     */
-    private boolean mAllowRestrictedBucket;
-
     private volatile boolean mAppIdleEnabled;
     private volatile boolean mIsCharging;
     private boolean mSystemServicesReady = false;
@@ -1058,13 +1050,6 @@
                         Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
                     }
                 }
-                if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
-                    newBucket = STANDBY_BUCKET_RARE;
-                    // Leave the reason alone.
-                    if (DEBUG) {
-                        Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch");
-                    }
-                }
                 if (newBucket > minBucket) {
                     newBucket = minBucket;
                     // Leave the reason alone.
@@ -1689,7 +1674,7 @@
 
         final int reason = (REASON_MAIN_MASK & mainReason) | (REASON_SUB_MASK & restrictReason);
         final long nowElapsed = mInjector.elapsedRealtime();
-        final int bucket = mAllowRestrictedBucket ? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE;
+        final int bucket = STANDBY_BUCKET_RESTRICTED;
         setAppStandbyBucket(packageName, userId, bucket, reason, nowElapsed, false);
     }
 
@@ -1793,9 +1778,6 @@
                 Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
                 return;
             }
-            if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
-                newBucket = STANDBY_BUCKET_RARE;
-            }
             AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
                     userId, elapsedRealtime);
             boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
@@ -1921,7 +1903,6 @@
                                 + " due to min timeout");
                     }
                 } else if (newBucket == STANDBY_BUCKET_RARE
-                        && mAllowRestrictedBucket
                         && getBucketForLocked(packageName, userId, elapsedRealtime)
                         == STANDBY_BUCKET_RESTRICTED) {
                     // Prediction doesn't think the app will be used anytime soon and
@@ -2529,8 +2510,6 @@
 
         pw.println();
         pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
-        pw.print(" mAllowRestrictedBucket=");
-        pw.print(mAllowRestrictedBucket);
         pw.print(" mIsCharging=");
         pw.print(mIsCharging);
         pw.println();
@@ -2711,12 +2690,6 @@
             }
         }
 
-        boolean isRestrictedBucketEnabled() {
-            return Global.getInt(mContext.getContentResolver(),
-                    Global.ENABLE_RESTRICTED_BUCKET,
-                    Global.DEFAULT_ENABLE_RESTRICTED_BUCKET) == 1;
-        }
-
         File getDataSystemDirectory() {
             return Environment.getDataSystemDirectory();
         }
@@ -3079,11 +3052,6 @@
             // APP_STANDBY_ENABLED is a SystemApi that some apps may be watching, so best to
             // leave it in Settings.
             cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
-            // Leave ENABLE_RESTRICTED_BUCKET as a user-controlled setting which will stay in
-            // Settings.
-            // TODO: make setting user-specific
-            cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET),
-                    false, this);
             // ADAPTIVE_BATTERY_MANAGEMENT_ENABLED is a user setting, so it has to stay in Settings.
             cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
                     false, this);
@@ -3284,10 +3252,6 @@
                                 Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
             }
 
-            synchronized (mAppIdleLock) {
-                mAllowRestrictedBucket = mInjector.isRestrictedBucketEnabled();
-            }
-
             setAppIdleEnabled(mInjector.isAppIdleEnabled());
         }
 
diff --git a/api/Android.bp b/api/Android.bp
index 73dbd28..24b3004 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -41,23 +41,6 @@
 }
 
 python_binary_host {
-    name: "api_versions_trimmer",
-    srcs: ["api_versions_trimmer.py"],
-}
-
-python_test_host {
-    name: "api_versions_trimmer_unittests",
-    main: "api_versions_trimmer_unittests.py",
-    srcs: [
-        "api_versions_trimmer_unittests.py",
-        "api_versions_trimmer.py",
-    ],
-    test_options: {
-        unit_test: true,
-    },
-}
-
-python_binary_host {
     name: "merge_annotation_zips",
     srcs: ["merge_annotation_zips.py"],
 }
diff --git a/api/api.go b/api/api.go
index 9876abb..09c2383 100644
--- a/api/api.go
+++ b/api/api.go
@@ -194,55 +194,6 @@
 	}
 }
 
-func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
-	// For the filtered api versions, we prune all APIs except art module's APIs. because
-	// 1) ART apis are available by default to all modules, while other module-to-module deps are
-	//    explicit and probably receive more scrutiny anyway
-	// 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
-	// 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
-	//    per-module lint databases that excludes just that module's APIs. Alas, that's more
-	//    difficult to achieve.
-	modules = remove(modules, art)
-
-	for _, i := range []struct{
-		name string
-		out  string
-		in   string
-	}{
-		{
-			// We shouldn't need public-filtered or system-filtered.
-			// public-filtered is currently used to lint things that
-			// use the module sdk or the system server sdk, but those
-			// should be switched over to module-filtered and
-			// system-server-filtered, and then public-filtered can
-			// be removed.
-			name: "api-versions-xml-public-filtered",
-			out:  "api-versions-public-filtered.xml",
-			in:   ":api_versions_public{.api_versions.xml}",
-		}, {
-			name: "api-versions-xml-module-lib-filtered",
-			out:  "api-versions-module-lib-filtered.xml",
-			in:   ":api_versions_module_lib{.api_versions.xml}",
-		}, {
-			name: "api-versions-xml-system-server-filtered",
-			out:  "api-versions-system-server-filtered.xml",
-			in:   ":api_versions_system_server{.api_versions.xml}",
-		},
-	} {
-		props := genruleProps{}
-		props.Name = proptools.StringPtr(i.name)
-		props.Out = []string{i.out}
-		// Note: order matters: first parameter is the full api-versions.xml
-		// after that the stubs files in any order
-		// stubs files are all modules that export API surfaces EXCEPT ART
-		props.Srcs = append([]string{i.in}, createSrcs(modules, ".stubs{.jar}")...)
-		props.Tools = []string{"api_versions_trimmer"}
-		props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
-		props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
-		ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable)
-	}
-}
-
 func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) {
 	props := libraryProps{}
 	props.Name = proptools.StringPtr("all-modules-public-stubs")
@@ -395,8 +346,6 @@
 
 	createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
 
-	createFilteredApiVersions(ctx, bootclasspath)
-
 	createPublicStubsSourceFilegroup(ctx, bootclasspath)
 }
 
diff --git a/api/api_versions_trimmer.py b/api/api_versions_trimmer.py
deleted file mode 100755
index 9afd95a..0000000
--- a/api/api_versions_trimmer.py
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Script to remove mainline APIs from the api-versions.xml."""
-
-import argparse
-import re
-import xml.etree.ElementTree as ET
-import zipfile
-
-
-def read_classes(stubs):
-  """Read classes from the stubs file.
-
-  Args:
-    stubs: argument can be a path to a file (a string), a file-like object or a
-    path-like object
-
-  Returns:
-    a set of the classes found in the file (set of strings)
-  """
-  classes = set()
-  with zipfile.ZipFile(stubs) as z:
-    for info in z.infolist():
-      if (not info.is_dir()
-          and info.filename.endswith(".class")
-          and not info.filename.startswith("META-INF")):
-        # drop ".class" extension
-        classes.add(info.filename[:-6])
-  return classes
-
-
-def filter_method_tag(method, classes_to_remove):
-  """Updates the signature of this method by calling filter_method_signature.
-
-  Updates the method passed into this function.
-
-  Args:
-    method: xml element that represents a method
-    classes_to_remove: set of classes you to remove
-  """
-  filtered = filter_method_signature(method.get("name"), classes_to_remove)
-  method.set("name", filtered)
-
-
-def filter_method_signature(signature, classes_to_remove):
-  """Removes mentions of certain classes from this method signature.
-
-  Replaces any existing classes that need to be removed, with java/lang/Object
-
-  Args:
-    signature: string that is a java representation of a method signature
-    classes_to_remove: set of classes you to remove
-  """
-  regex = re.compile("L.*?;")
-  start = signature.find("(")
-  matches = set(regex.findall(signature[start:]))
-  for m in matches:
-    # m[1:-1] to drop the leading `L` and `;` ending
-    if m[1:-1] in classes_to_remove:
-      signature = signature.replace(m, "Ljava/lang/Object;")
-  return signature
-
-
-def filter_lint_database(database, classes_to_remove, output):
-  """Reads a lint database and writes a filtered version without some classes.
-
-  Reads database from api-versions.xml and removes any references to classes
-  in the second argument. Writes the result (another xml with the same format
-  of the database) to output.
-
-  Args:
-    database: path to xml with lint database to read
-    classes_to_remove: iterable (ideally a set or similar for quick
-    lookups) that enumerates the classes that should be removed
-    output: path to write the filtered database
-  """
-  xml = ET.parse(database)
-  root = xml.getroot()
-  for c in xml.findall("class"):
-    cname = c.get("name")
-    if cname in classes_to_remove:
-      root.remove(c)
-    else:
-      # find the <extends /> tag inside this class to see if the parent
-      # has been removed from the known classes (attribute called name)
-      super_classes = c.findall("extends")
-      for super_class in super_classes:
-        super_class_name = super_class.get("name")
-        if super_class_name in classes_to_remove:
-          super_class.set("name", "java/lang/Object")
-      interfaces = c.findall("implements")
-      for interface in interfaces:
-        interface_name = interface.get("name")
-        if interface_name in classes_to_remove:
-          c.remove(interface)
-      for method in c.findall("method"):
-        filter_method_tag(method, classes_to_remove)
-  xml.write(output)
-
-
-def main():
-  """Run the program."""
-  parser = argparse.ArgumentParser(
-      description=
-      ("Read a lint database (api-versions.xml) and many stubs jar files. "
-       "Produce another database file that doesn't include the classes present "
-       "in the stubs file(s)."))
-  parser.add_argument("output", help="Destination of the result (xml file).")
-  parser.add_argument(
-      "api_versions",
-      help="The lint database (api-versions.xml file) to read data from"
-  )
-  parser.add_argument("stubs", nargs="+", help="The stubs jar file(s)")
-  parsed = parser.parse_args()
-  classes = set()
-  for stub in parsed.stubs:
-    classes.update(read_classes(stub))
-  filter_lint_database(parsed.api_versions, classes, parsed.output)
-
-
-if __name__ == "__main__":
-  main()
diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py
deleted file mode 100644
index d2e5b7d..0000000
--- a/api/api_versions_trimmer_unittests.py
+++ /dev/null
@@ -1,307 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import io
-import re
-import unittest
-import xml.etree.ElementTree as ET
-import zipfile
-
-import api_versions_trimmer
-
-
-def create_in_memory_zip_file(files):
-  f = io.BytesIO()
-  with zipfile.ZipFile(f, "w") as z:
-    for fname in files:
-      with z.open(fname, mode="w") as class_file:
-        class_file.write(b"")
-  return f
-
-
-def indent(elem, level=0):
-  i = "\n" + level * "  "
-  j = "\n" + (level - 1) * "  "
-  if len(elem):
-    if not elem.text or not elem.text.strip():
-      elem.text = i + "  "
-      if not elem.tail or not elem.tail.strip():
-        elem.tail = i
-        for subelem in elem:
-          indent(subelem, level + 1)
-        if not elem.tail or not elem.tail.strip():
-          elem.tail = j
-    else:
-      if level and (not elem.tail or not elem.tail.strip()):
-        elem.tail = j
-    return elem
-
-
-def pretty_print(s):
-  tree = ET.parse(io.StringIO(s))
-  el = indent(tree.getroot())
-  res = ET.tostring(el).decode("utf-8")
-  # remove empty lines inside the result because this still breaks some
-  # comparisons
-  return re.sub(r"\n\s*\n", "\n", res, re.MULTILINE)
-
-
-class ApiVersionsTrimmerUnittests(unittest.TestCase):
-
-  def setUp(self):
-    # so it prints diffs in long strings (xml files)
-    self.maxDiff = None
-
-  def test_read_classes(self):
-    f = create_in_memory_zip_file(
-        ["a/b/C.class",
-         "a/b/D.class",
-        ]
-    )
-    res = api_versions_trimmer.read_classes(f)
-    self.assertEqual({"a/b/C", "a/b/D"}, res)
-
-  def test_read_classes_ignore_dex(self):
-    f = create_in_memory_zip_file(
-        ["a/b/C.class",
-         "a/b/D.class",
-         "a/b/E.dex",
-         "f.dex",
-        ]
-    )
-    res = api_versions_trimmer.read_classes(f)
-    self.assertEqual({"a/b/C", "a/b/D"}, res)
-
-  def test_read_classes_ignore_manifest(self):
-    f = create_in_memory_zip_file(
-        ["a/b/C.class",
-         "a/b/D.class",
-         "META-INFO/G.class"
-        ]
-    )
-    res = api_versions_trimmer.read_classes(f)
-    self.assertEqual({"a/b/C", "a/b/D"}, res)
-
-  def test_filter_method_signature(self):
-    xml = """
-    <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
-    """
-    method = ET.fromstring(xml)
-    classes_to_remove = {"android/accessibilityservice/GestureDescription"}
-    expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
-    api_versions_trimmer.filter_method_tag(method, classes_to_remove)
-    self.assertEqual(expected, method.get("name"))
-
-  def test_filter_method_signature_with_L_in_method(self):
-    xml = """
-    <method name="dispatchLeftGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
-    """
-    method = ET.fromstring(xml)
-    classes_to_remove = {"android/accessibilityservice/GestureDescription"}
-    expected = "dispatchLeftGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
-    api_versions_trimmer.filter_method_tag(method, classes_to_remove)
-    self.assertEqual(expected, method.get("name"))
-
-  def test_filter_method_signature_with_L_in_class(self):
-    xml = """
-    <method name="dispatchGesture(Landroid/accessibilityservice/LeftGestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
-    """
-    method = ET.fromstring(xml)
-    classes_to_remove = {"android/accessibilityservice/LeftGestureDescription"}
-    expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
-    api_versions_trimmer.filter_method_tag(method, classes_to_remove)
-    self.assertEqual(expected, method.get("name"))
-
-  def test_filter_method_signature_with_inner_class(self):
-    xml = """
-    <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription$Inner;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
-    """
-    method = ET.fromstring(xml)
-    classes_to_remove = {"android/accessibilityservice/GestureDescription$Inner"}
-    expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
-    api_versions_trimmer.filter_method_tag(method, classes_to_remove)
-    self.assertEqual(expected, method.get("name"))
-
-  def _run_filter_db_test(self, database_str, expected):
-    """Performs the pattern of testing the filter_lint_database method.
-
-    Filters instances of the class "a/b/C" (hard-coded) from the database string
-    and compares the result with the expected result (performs formatting of
-    the xml of both inputs)
-
-    Args:
-      database_str: string, the contents of the lint database (api-versions.xml)
-      expected: string, the expected result after filtering the original
-    database
-    """
-    database = io.StringIO(database_str)
-    classes_to_remove = {"a/b/C"}
-    output = io.BytesIO()
-    api_versions_trimmer.filter_lint_database(
-        database,
-        classes_to_remove,
-        output
-    )
-    expected = pretty_print(expected)
-    res = pretty_print(output.getvalue().decode("utf-8"))
-    self.assertEqual(expected, res)
-
-  def test_filter_lint_database_updates_method_signature_params(self):
-    self._run_filter_db_test(
-        database_str="""
-    <api version="2">
-      <!-- will be removed -->
-      <class name="a/b/C" since="1">
-        <extends name="java/lang/Object"/>
-      </class>
-
-      <class name="a/b/E" since="1">
-        <!-- extends will be modified -->
-        <extends name="a/b/C"/>
-        <!-- first parameter will be modified -->
-        <method name="dispatchGesture(La/b/C;Landroid/os/Handler;)Z" since="24"/>
-        <!-- second should remain untouched -->
-        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
-      </class>
-    </api>
-    """,
-        expected="""
-    <api version="2">
-      <class name="a/b/E" since="1">
-        <extends name="java/lang/Object"/>
-        <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
-        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
-      </class>
-    </api>
-    """)
-
-  def test_filter_lint_database_updates_method_signature_return(self):
-    self._run_filter_db_test(
-        database_str="""
-    <api version="2">
-      <!-- will be removed -->
-      <class name="a/b/C" since="1">
-        <extends name="java/lang/Object"/>
-      </class>
-
-      <class name="a/b/E" since="1">
-        <!-- extends will be modified -->
-        <extends name="a/b/C"/>
-        <!-- return type should be changed -->
-        <method name="gestureIdToString(I)La/b/C;" since="24"/>
-      </class>
-    </api>
-    """,
-        expected="""
-    <api version="2">
-      <class name="a/b/E" since="1">
-
-        <extends name="java/lang/Object"/>
-
-        <method name="gestureIdToString(I)Ljava/lang/Object;" since="24"/>
-      </class>
-    </api>
-    """)
-
-  def test_filter_lint_database_removes_implements(self):
-    self._run_filter_db_test(
-        database_str="""
-    <api version="2">
-      <!-- will be removed -->
-      <class name="a/b/C" since="1">
-        <extends name="java/lang/Object"/>
-      </class>
-
-      <class name="a/b/D" since="1">
-        <extends name="java/lang/Object"/>
-        <implements name="a/b/C"/>
-        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
-      </class>
-    </api>
-    """,
-        expected="""
-    <api version="2">
-
-      <class name="a/b/D" since="1">
-        <extends name="java/lang/Object"/>
-        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
-      </class>
-    </api>
-    """)
-
-  def test_filter_lint_database_updates_extends(self):
-    self._run_filter_db_test(
-        database_str="""
-    <api version="2">
-      <!-- will be removed -->
-      <class name="a/b/C" since="1">
-        <extends name="java/lang/Object"/>
-      </class>
-
-      <class name="a/b/E" since="1">
-        <!-- extends will be modified -->
-        <extends name="a/b/C"/>
-        <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
-        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
-      </class>
-    </api>
-    """,
-        expected="""
-    <api version="2">
-      <class name="a/b/E" since="1">
-        <extends name="java/lang/Object"/>
-        <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
-        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
-      </class>
-    </api>
-    """)
-
-  def test_filter_lint_database_removes_class(self):
-    self._run_filter_db_test(
-        database_str="""
-    <api version="2">
-      <!-- will be removed -->
-      <class name="a/b/C" since="1">
-        <extends name="java/lang/Object"/>
-      </class>
-
-      <class name="a/b/D" since="1">
-        <extends name="java/lang/Object"/>
-        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
-      </class>
-    </api>
-    """,
-        expected="""
-    <api version="2">
-
-      <class name="a/b/D" since="1">
-        <extends name="java/lang/Object"/>
-        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
-      </class>
-    </api>
-    """)
-
-
-if __name__ == "__main__":
-  unittest.main(verbosity=2)
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 00d9a4b..c4e8b0e 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -458,6 +458,7 @@
 
 EGLConfig BootAnimation::getEglConfig(const EGLDisplay& display) {
     const EGLint attribs[] = {
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
         EGL_RED_SIZE,   8,
         EGL_GREEN_SIZE, 8,
         EGL_BLUE_SIZE,  8,
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 957ebfb..a7560b2 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -40,7 +40,7 @@
     public String longHelp() {
         return shortHelp() + "\n"
                 + "\n"
-                + "usage: svc power stayon [true|false|usb|ac|wireless]\n"
+                + "usage: svc power stayon [true|false|usb|ac|wireless|dock]\n"
                 + "         Set the 'keep awake while plugged in' setting.\n"
                 + "       svc power reboot [reason]\n"
                 + "         Perform a runtime shutdown and reboot device with specified reason.\n"
@@ -66,9 +66,10 @@
                 if ("stayon".equals(args[1]) && args.length == 3) {
                     int val;
                     if ("true".equals(args[2])) {
-                        val = BatteryManager.BATTERY_PLUGGED_AC |
-                                BatteryManager.BATTERY_PLUGGED_USB |
-                                BatteryManager.BATTERY_PLUGGED_WIRELESS;
+                        val = BatteryManager.BATTERY_PLUGGED_AC
+                                | BatteryManager.BATTERY_PLUGGED_USB
+                                | BatteryManager.BATTERY_PLUGGED_WIRELESS
+                                | BatteryManager.BATTERY_PLUGGED_DOCK;
                     }
                     else if ("false".equals(args[2])) {
                         val = 0;
@@ -78,6 +79,8 @@
                         val = BatteryManager.BATTERY_PLUGGED_AC;
                     } else if ("wireless".equals(args[2])) {
                         val = BatteryManager.BATTERY_PLUGGED_WIRELESS;
+                    } else if ("dock".equals(args[2])) {
+                        val = BatteryManager.BATTERY_PLUGGED_DOCK;
                     } else {
                         break fail;
                     }
diff --git a/core/api/current.txt b/core/api/current.txt
index 06e3160..70dcd54 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -83,6 +83,7 @@
     field public static final String CLEAR_APP_CACHE = "android.permission.CLEAR_APP_CACHE";
     field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
     field public static final String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
+    field public static final String CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS = "android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS";
     field public static final String CREDENTIAL_MANAGER_SET_ORIGIN = "android.permission.CREDENTIAL_MANAGER_SET_ORIGIN";
     field public static final String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES";
     field public static final String DELETE_PACKAGES = "android.permission.DELETE_PACKAGES";
@@ -556,6 +557,7 @@
     field public static final int canTakeScreenshot = 16844303; // 0x101060f
     field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
     field public static final int cantSaveState = 16844142; // 0x101056e
+    field public static final int capability;
     field @Deprecated public static final int capitalize = 16843113; // 0x1010169
     field public static final int category = 16843752; // 0x10103e8
     field public static final int centerBright = 16842956; // 0x10100cc
@@ -1448,6 +1450,7 @@
     field public static final int sessionService = 16843837; // 0x101043d
     field public static final int settingsActivity = 16843301; // 0x1010225
     field public static final int settingsSliceUri = 16844179; // 0x1010593
+    field public static final int settingsSubtitle;
     field public static final int setupActivity = 16843766; // 0x10103f6
     field public static final int shadowColor = 16843105; // 0x1010161
     field public static final int shadowDx = 16843106; // 0x1010162
@@ -13672,8 +13675,9 @@
   }
 
   public final class CredentialOption implements android.os.Parcelable {
-    ctor public CredentialOption(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean);
+    ctor @Deprecated public CredentialOption(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean);
     method public int describeContents();
+    method @NonNull public java.util.Set<android.content.ComponentName> getAllowedProviders();
     method @NonNull public android.os.Bundle getCandidateQueryData();
     method @NonNull public android.os.Bundle getCredentialRetrievalData();
     method @NonNull public String getType();
@@ -13683,6 +13687,14 @@
     field public static final String FLATTENED_REQUEST = "android.credentials.GetCredentialOption.FLATTENED_REQUEST_STRING";
   }
 
+  public static final class CredentialOption.Builder {
+    ctor public CredentialOption.Builder(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle);
+    method @NonNull public android.credentials.CredentialOption.Builder addAllowedProvider(@NonNull android.content.ComponentName);
+    method @NonNull public android.credentials.CredentialOption build();
+    method @NonNull public android.credentials.CredentialOption.Builder setAllowedProviders(@NonNull java.util.Set<android.content.ComponentName>);
+    method @NonNull public android.credentials.CredentialOption.Builder setIsSystemProviderRequired(boolean);
+  }
+
   public class GetCredentialException extends java.lang.Exception {
     ctor public GetCredentialException(@NonNull String, @Nullable String);
     ctor public GetCredentialException(@NonNull String, @Nullable String, @Nullable Throwable);
@@ -40707,7 +40719,7 @@
     method public abstract void onBeginGetCredential(@NonNull android.service.credentials.BeginGetCredentialRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException>);
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onClearCredentialState(@NonNull android.service.credentials.ClearCredentialStateRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>);
-    field public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
+    field @Deprecated public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
     field public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST";
     field public static final String EXTRA_BEGIN_GET_CREDENTIAL_RESPONSE = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_RESPONSE";
     field public static final String EXTRA_CREATE_CREDENTIAL_EXCEPTION = "android.service.credentials.extra.CREATE_CREDENTIAL_EXCEPTION";
@@ -40717,6 +40729,7 @@
     field public static final String EXTRA_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.GET_CREDENTIAL_REQUEST";
     field public static final String EXTRA_GET_CREDENTIAL_RESPONSE = "android.service.credentials.extra.GET_CREDENTIAL_RESPONSE";
     field public static final String SERVICE_INTERFACE = "android.service.credentials.CredentialProviderService";
+    field public static final String SERVICE_META_DATA = "android.credentials.provider";
   }
 
   public final class GetCredentialRequest implements android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b7d97ea..51863bb 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1112,6 +1112,7 @@
     method @Nullable public CharSequence getLabel(@NonNull android.content.Context);
     method @Nullable public android.graphics.drawable.Drawable getServiceIcon(@NonNull android.content.Context);
     method @NonNull public android.content.pm.ServiceInfo getServiceInfo();
+    method @Nullable public CharSequence getSettingsSubtitle();
     method @NonNull public boolean hasCapability(@NonNull String);
     method public boolean isEnabled();
     method public boolean isSystemProvider();
@@ -1124,6 +1125,7 @@
     method @NonNull public android.credentials.CredentialProviderInfo.Builder addCapabilities(@NonNull java.util.List<java.lang.String>);
     method @NonNull public android.credentials.CredentialProviderInfo build();
     method @NonNull public android.credentials.CredentialProviderInfo.Builder setEnabled(boolean);
+    method @NonNull public android.credentials.CredentialProviderInfo.Builder setSettingsSubtitle(@Nullable CharSequence);
     method @NonNull public android.credentials.CredentialProviderInfo.Builder setSystemProvider(boolean);
   }
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 502ef0d..99de724 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3269,6 +3269,43 @@
     /**
      * @hide
      */
+    public static boolean areIconsDifferent(Notification first, Notification second) {
+        return areIconsMaybeDifferent(first.getSmallIcon(), second.getSmallIcon())
+                || areIconsMaybeDifferent(first.getLargeIcon(), second.getLargeIcon());
+    }
+
+    /**
+     * Note that we aren't actually comparing the contents of the bitmaps here; this is only a
+     * cursory inspection. We will not return false negatives, but false positives are likely.
+     */
+    private static boolean areIconsMaybeDifferent(Icon a, Icon b) {
+        if (a == b) {
+            return false;
+        }
+        if (a == null || b == null) {
+            return true;
+        }
+        if (a.sameAs(b)) {
+            return false;
+        }
+        final int aType = a.getType();
+        if (aType != b.getType()) {
+            return true;
+        }
+        if (aType == Icon.TYPE_BITMAP || aType == Icon.TYPE_ADAPTIVE_BITMAP) {
+            final Bitmap aBitmap = a.getBitmap();
+            final Bitmap bBitmap = b.getBitmap();
+            return aBitmap.getWidth() != bBitmap.getWidth()
+                    || aBitmap.getHeight() != bBitmap.getHeight()
+                    || aBitmap.getConfig() != bBitmap.getConfig()
+                    || aBitmap.getGenerationId() != bBitmap.getGenerationId();
+        }
+        return true;
+    }
+
+    /**
+     * @hide
+     */
     public static boolean areStyledNotificationsVisiblyDifferent(Builder first, Builder second) {
         if (first.getStyle() == null) {
             return second.getStyle() != null;
@@ -7643,8 +7680,6 @@
 
         /**
          * @hide
-         * Note that we aren't actually comparing the contents of the bitmaps here, so this
-         * is only doing a cursory inspection. Bitmaps of equal size will appear the same.
          */
         @Override
         public boolean areNotificationsVisiblyDifferent(Style other) {
@@ -7652,32 +7687,7 @@
                 return true;
             }
             BigPictureStyle otherS = (BigPictureStyle) other;
-            return areIconsObviouslyDifferent(getBigPicture(), otherS.getBigPicture());
-        }
-
-        private static boolean areIconsObviouslyDifferent(Icon a, Icon b) {
-            if (a == b) {
-                return false;
-            }
-            if (a == null || b == null) {
-                return true;
-            }
-            if (a.sameAs(b)) {
-                return false;
-            }
-            final int aType = a.getType();
-            if (aType != b.getType()) {
-                return true;
-            }
-            if (aType == Icon.TYPE_BITMAP || aType == Icon.TYPE_ADAPTIVE_BITMAP) {
-                final Bitmap aBitmap = a.getBitmap();
-                final Bitmap bBitmap = b.getBitmap();
-                return aBitmap.getWidth() != bBitmap.getWidth()
-                        || aBitmap.getHeight() != bBitmap.getHeight()
-                        || aBitmap.getConfig() != bBitmap.getConfig()
-                        || aBitmap.getGenerationId() != bBitmap.getGenerationId();
-            }
-            return true;
+            return areIconsMaybeDifferent(getBigPicture(), otherS.getBigPicture());
         }
     }
 
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 303ada0..404f94a 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -188,6 +188,14 @@
     public int launchIntoPipHostTaskId;
 
     /**
+     * The task id of the parent Task of the launch-into-pip Activity, i.e., if task have more than
+     * one activity it will create new task for this activity, this id is the origin task id and
+     * the pip activity will be reparent to origin task when it exit pip mode.
+     * @hide
+     */
+    public int lastParentTaskIdBeforePip;
+
+    /**
      * The {@link Rect} copied from {@link DisplayCutout#getSafeInsets()} if the cutout is not of
      * (LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS),
      * {@code null} otherwise.
@@ -512,6 +520,7 @@
         pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR);
         shouldDockBigOverlays = source.readBoolean();
         launchIntoPipHostTaskId = source.readInt();
+        lastParentTaskIdBeforePip = source.readInt();
         displayCutoutInsets = source.readTypedObject(Rect.CREATOR);
         topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
         isResizeable = source.readBoolean();
@@ -558,6 +567,7 @@
         dest.writeTypedObject(pictureInPictureParams, flags);
         dest.writeBoolean(shouldDockBigOverlays);
         dest.writeInt(launchIntoPipHostTaskId);
+        dest.writeInt(lastParentTaskIdBeforePip);
         dest.writeTypedObject(displayCutoutInsets, flags);
         dest.writeTypedObject(topActivityInfo, flags);
         dest.writeBoolean(isResizeable);
@@ -598,6 +608,7 @@
                 + " pictureInPictureParams=" + pictureInPictureParams
                 + " shouldDockBigOverlays=" + shouldDockBigOverlays
                 + " launchIntoPipHostTaskId=" + launchIntoPipHostTaskId
+                + " lastParentTaskIdBeforePip=" + lastParentTaskIdBeforePip
                 + " displayCutoutSafeInsets=" + displayCutoutInsets
                 + " topActivityInfo=" + topActivityInfo
                 + " launchCookies=" + launchCookies
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index d3502c5..d2dbd64 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -309,14 +309,30 @@
             try {
                 if (checkGetTypePermission(attributionSource, uri)
                         == PermissionChecker.PERMISSION_GRANTED) {
-                    final String type = mInterface.getType(uri);
+                    String type;
+                    if (checkPermission(Manifest.permission.GET_ANY_PROVIDER_TYPE,
+                            attributionSource) == PermissionChecker.PERMISSION_GRANTED) {
+                        /*
+                        For calling packages having the special permission for any type,
+                        the calling identity should be cleared before calling getType.
+                         */
+                        final CallingIdentity origId = getContentProvider().clearCallingIdentity();
+                        try {
+                            type = mInterface.getType(uri);
+                        } finally {
+                            getContentProvider().restoreCallingIdentity(origId);
+                        }
+                    } else {
+                        type = mInterface.getType(uri);
+                    }
+
                     if (type != null) {
                         logGetTypeData(Binder.getCallingUid(), uri, type, true);
                     }
                     return type;
                 } else {
                     final int callingUid = Binder.getCallingUid();
-                    final long origId = Binder.clearCallingIdentity();
+                    final CallingIdentity origId = getContentProvider().clearCallingIdentity();
                     try {
                         final String type = getTypeAnonymous(uri);
                         if (type != null) {
@@ -324,7 +340,7 @@
                         }
                         return type;
                     } finally {
-                        Binder.restoreCallingIdentity(origId);
+                        getContentProvider().restoreCallingIdentity(origId);
                     }
                 }
             } catch (RemoteException e) {
diff --git a/core/java/android/credentials/CredentialOption.java b/core/java/android/credentials/CredentialOption.java
index 9a3b46d..da6656a 100644
--- a/core/java/android/credentials/CredentialOption.java
+++ b/core/java/android/credentials/CredentialOption.java
@@ -16,16 +16,25 @@
 
 package android.credentials;
 
+import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS;
+
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.ComponentName;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArraySet;
+
+import androidx.annotation.RequiresPermission;
 
 import com.android.internal.util.AnnotationValidations;
 import com.android.internal.util.Preconditions;
 
+import java.util.Set;
+
 /**
  * Information about a specific type of credential to be requested during a {@link
  * CredentialManager#getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor,
@@ -66,6 +75,14 @@
     private final boolean mIsSystemProviderRequired;
 
     /**
+     * A list of {@link ComponentName}s corresponding to the providers that this option must be
+     * queried against.
+     */
+    @NonNull
+    private final ArraySet<ComponentName> mAllowedProviders;
+
+
+    /**
      * Returns the requested credential type.
      */
     @NonNull
@@ -105,12 +122,22 @@
         return mIsSystemProviderRequired;
     }
 
+    /**
+     * Returns the set of {@link ComponentName} corresponding to providers that must receive
+     * this option.
+     */
+    @NonNull
+    public Set<ComponentName> getAllowedProviders() {
+        return mAllowedProviders;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString8(mType);
         dest.writeBundle(mCredentialRetrievalData);
         dest.writeBundle(mCandidateQueryData);
         dest.writeBoolean(mIsSystemProviderRequired);
+        dest.writeArraySet(mAllowedProviders);
     }
 
     @Override
@@ -125,6 +152,7 @@
                 + ", requestData=" + mCredentialRetrievalData
                 + ", candidateQueryData=" + mCandidateQueryData
                 + ", isSystemProviderRequired=" + mIsSystemProviderRequired
+                + ", allowedProviders=" + mAllowedProviders
                 + "}";
     }
 
@@ -139,17 +167,50 @@
      *                                 provider
      * @throws IllegalArgumentException If type is empty.
      */
-    public CredentialOption(
+    private CredentialOption(
             @NonNull String type,
             @NonNull Bundle credentialRetrievalData,
             @NonNull Bundle candidateQueryData,
-            boolean isSystemProviderRequired) {
+            boolean isSystemProviderRequired,
+            @NonNull ArraySet<ComponentName> allowedProviders) {
         mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
         mCredentialRetrievalData = requireNonNull(credentialRetrievalData,
                 "requestData must not be null");
         mCandidateQueryData = requireNonNull(candidateQueryData,
                 "candidateQueryData must not be null");
         mIsSystemProviderRequired = isSystemProviderRequired;
+        mAllowedProviders = requireNonNull(allowedProviders, "providerFilterSer must"
+                + "not be empty");
+    }
+
+    /**
+     * Constructs a {@link CredentialOption}.
+     *
+     * @param type                     the requested credential type
+     * @param credentialRetrievalData  the request data
+     * @param candidateQueryData       the partial request data that will be sent to the provider
+     *                                 during the initial credential candidate query stage
+     * @param isSystemProviderRequired whether the request must only be fulfilled by a system
+     *                                 provider
+     * @throws IllegalArgumentException If type is empty, or null.
+     * @throws NullPointerException If {@code credentialRetrievalData}, or
+     * {@code candidateQueryData} is null.
+     *
+     * @deprecated replaced by Builder
+     */
+    @Deprecated
+    public CredentialOption(
+            @NonNull String type,
+            @NonNull Bundle credentialRetrievalData,
+            @NonNull Bundle candidateQueryData,
+            boolean isSystemProviderRequired) {
+        this(
+                type,
+                credentialRetrievalData,
+                candidateQueryData,
+                isSystemProviderRequired,
+                new ArraySet<>()
+        );
     }
 
     private CredentialOption(@NonNull Parcel in) {
@@ -165,6 +226,8 @@
         mCandidateQueryData = candidateQueryData;
         AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData);
         mIsSystemProviderRequired = isSystemProviderRequired;
+        mAllowedProviders = (ArraySet<ComponentName>) in.readArraySet(null);
+        AnnotationValidations.validate(NonNull.class, null, mAllowedProviders);
     }
 
     @NonNull
@@ -179,4 +242,108 @@
             return new CredentialOption(in);
         }
     };
+
+    /** A builder for {@link CredentialOption}. */
+    public static final class Builder {
+
+        @NonNull
+        private String mType;
+
+        @NonNull
+        private Bundle mCredentialRetrievalData;
+
+        @NonNull
+        private Bundle mCandidateQueryData;
+
+        private boolean mIsSystemProviderRequired = false;
+
+        @NonNull
+        private ArraySet<ComponentName> mAllowedProviders = new ArraySet<>();
+
+        /**
+         * @param type                    the type of the credential option
+         * @param credentialRetrievalData the full request data
+         * @param candidateQueryData      the partial request data that will be sent to the provider
+         *                                during the initial credential candidate query stage.
+         * @throws IllegalArgumentException If {@code type} is null, or empty
+         * @throws NullPointerException     If {@code credentialRetrievalData}, or
+         *                                  {@code candidateQueryData} is null
+         */
+        public Builder(@NonNull String type, @NonNull Bundle credentialRetrievalData,
+                @NonNull Bundle candidateQueryData) {
+            mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+                    + "null, or empty");
+            mCredentialRetrievalData = requireNonNull(credentialRetrievalData,
+                    "credentialRetrievalData must not be null");
+            mCandidateQueryData = requireNonNull(candidateQueryData,
+                    "candidateQueryData must not be null");
+        }
+
+        /**
+         * Sets a true/false value corresponding to whether this option must be serviced by
+         * system credentials providers only.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setIsSystemProviderRequired(boolean isSystemProviderRequired) {
+            mIsSystemProviderRequired = isSystemProviderRequired;
+            return this;
+        }
+
+        /**
+         * Adds a provider {@link ComponentName} to be queried while gathering credentials from
+         * credential providers on the device.
+         *
+         * If no candidate providers are specified, all user configured and system credential
+         * providers will be queried in the candidate query phase.
+         *
+         * If an invalid component name is provided, or a service corresponding to the
+         * component name does not exist on the device, that component name is ignored.
+         * If all component names are invalid, or not present on the device, no providers
+         * are queried and no credentials are retrieved.
+         *
+         * @throws NullPointerException If {@code allowedProvider} is null
+         */
+        @RequiresPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)
+        @NonNull
+        public Builder addAllowedProvider(@NonNull ComponentName allowedProvider) {
+            mAllowedProviders.add(requireNonNull(allowedProvider,
+                    "allowedProvider must not be null"));
+            return this;
+        }
+
+        /**
+         * Sets a set of provider {@link ComponentName} to be queried while gathering credentials
+         * from credential providers on the device.
+         *
+         * If no candidate providers are specified, all user configured and system credential
+         * providers will be queried in the candidate query phase.
+         *
+         * If an invalid component name is provided, or a service corresponding to the
+         * component name does not exist on the device, that component name is ignored.
+         * If all component names are invalid, or not present on the device, no providers
+         * are queried and no credentials are retrieved.
+         *
+         * @throws NullPointerException If {@code allowedProviders} is null, or any of its
+         * elements are null.
+         */
+        @RequiresPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)
+        @NonNull
+        public Builder setAllowedProviders(@NonNull Set<ComponentName> allowedProviders) {
+            Preconditions.checkCollectionElementsNotNull(
+                    allowedProviders,
+                    /*valueName=*/ "allowedProviders");
+            mAllowedProviders = new ArraySet<>(allowedProviders);
+            return this;
+        }
+
+        /**
+         * Builds a {@link CredentialOption}.
+         */
+        @NonNull
+        public CredentialOption build() {
+            return new CredentialOption(mType, mCredentialRetrievalData, mCandidateQueryData,
+                    mIsSystemProviderRequired, mAllowedProviders);
+        }
+    }
 }
diff --git a/core/java/android/credentials/CredentialProviderInfo.java b/core/java/android/credentials/CredentialProviderInfo.java
index 7276770..c224f01 100644
--- a/core/java/android/credentials/CredentialProviderInfo.java
+++ b/core/java/android/credentials/CredentialProviderInfo.java
@@ -29,7 +29,9 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * {@link ServiceInfo} and meta-data about a credential provider.
@@ -39,8 +41,9 @@
 @TestApi
 public final class CredentialProviderInfo implements Parcelable {
     @NonNull private final ServiceInfo mServiceInfo;
-    @NonNull private final List<String> mCapabilities = new ArrayList<>();
+    @NonNull private final Set<String> mCapabilities = new HashSet<>();
     @Nullable private final CharSequence mOverrideLabel;
+    @Nullable private CharSequence mSettingsSubtitle = null;
     private final boolean mIsSystemProvider;
     private final boolean mIsEnabled;
 
@@ -53,6 +56,7 @@
         mServiceInfo = builder.mServiceInfo;
         mCapabilities.addAll(builder.mCapabilities);
         mIsSystemProvider = builder.mIsSystemProvider;
+        mSettingsSubtitle = builder.mSettingsSubtitle;
         mIsEnabled = builder.mIsEnabled;
         mOverrideLabel = builder.mOverrideLabel;
     }
@@ -92,7 +96,11 @@
     /** Returns a list of capabilities this provider service can support. */
     @NonNull
     public List<String> getCapabilities() {
-        return Collections.unmodifiableList(mCapabilities);
+        List<String> capabilities = new ArrayList<>();
+        for (String capability : mCapabilities) {
+            capabilities.add(capability);
+        }
+        return Collections.unmodifiableList(capabilities);
     }
 
     /** Returns whether the provider is enabled by the user. */
@@ -100,6 +108,12 @@
         return mIsEnabled;
     }
 
+    /** Returns the settings subtitle. */
+    @Nullable
+    public CharSequence getSettingsSubtitle() {
+        return mSettingsSubtitle;
+    }
+
     /** Returns the component name for the service. */
     @NonNull
     public ComponentName getComponentName() {
@@ -110,9 +124,12 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeTypedObject(mServiceInfo, flags);
         dest.writeBoolean(mIsSystemProvider);
-        dest.writeStringList(mCapabilities);
         dest.writeBoolean(mIsEnabled);
         TextUtils.writeToParcel(mOverrideLabel, dest, flags);
+        TextUtils.writeToParcel(mSettingsSubtitle, dest, flags);
+
+        List<String> capabilities = getCapabilities();
+        dest.writeStringList(capabilities);
     }
 
     @Override
@@ -135,6 +152,9 @@
                 + "overrideLabel="
                 + mOverrideLabel
                 + ", "
+                + "settingsSubtitle="
+                + mSettingsSubtitle
+                + ", "
                 + "capabilities="
                 + String.join(",", mCapabilities)
                 + "}";
@@ -143,9 +163,13 @@
     private CredentialProviderInfo(@NonNull Parcel in) {
         mServiceInfo = in.readTypedObject(ServiceInfo.CREATOR);
         mIsSystemProvider = in.readBoolean();
-        in.readStringList(mCapabilities);
         mIsEnabled = in.readBoolean();
         mOverrideLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mSettingsSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+
+        List<String> capabilities = new ArrayList<>();
+        in.readStringList(capabilities);
+        mCapabilities.addAll(capabilities);
     }
 
     public static final @NonNull Parcelable.Creator<CredentialProviderInfo> CREATOR =
@@ -165,8 +189,9 @@
     public static final class Builder {
 
         @NonNull private ServiceInfo mServiceInfo;
-        @NonNull private List<String> mCapabilities = new ArrayList<>();
+        @NonNull private Set<String> mCapabilities = new HashSet<>();
         private boolean mIsSystemProvider = false;
+        @Nullable private CharSequence mSettingsSubtitle = null;
         private boolean mIsEnabled = false;
         @Nullable private CharSequence mOverrideLabel = null;
 
@@ -195,12 +220,28 @@
             return this;
         }
 
+        /** Sets the settings subtitle. */
+        public @NonNull Builder setSettingsSubtitle(@Nullable CharSequence settingsSubtitle) {
+            mSettingsSubtitle = settingsSubtitle;
+            return this;
+        }
+
         /** Sets a list of capabilities this provider service can support. */
         public @NonNull Builder addCapabilities(@NonNull List<String> capabilities) {
             mCapabilities.addAll(capabilities);
             return this;
         }
 
+        /**
+         * Sets a list of capabilities this provider service can support.
+         *
+         * @hide
+         */
+        public @NonNull Builder addCapabilities(@NonNull Set<String> capabilities) {
+            mCapabilities.addAll(capabilities);
+            return this;
+        }
+
         /** Sets whether it is enabled by the user. */
         public @NonNull Builder setEnabled(boolean isEnabled) {
             mIsEnabled = isEnabled;
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index e9df553..1bc6099 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -661,7 +661,7 @@
      *
      * <p>Repeating burst requests are a simple way for an application to
      * maintain a preview or other continuous stream of frames where each
-     * request is different in a predicatable way, without having to continually
+     * request is different in a predictable way, without having to continually
      * submit requests through {@link #captureBurst}.</p>
      *
      * <p>To stop the repeating capture, call {@link #stopRepeating}. Any
@@ -902,7 +902,7 @@
      * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING OFFLINE_PROCESSING}
      * capability in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}. When this method
      * is supported, applications can use it to improve the latency of closing camera or recreating
-     * capture session without losing the in progresss capture request outputs.</p>
+     * capture session without losing the in progress capture request outputs.</p>
      *
      * <p>Offline processing mode and the corresponding {@link CameraOfflineSession} differ from
      * a regular online camera capture session in several ways. Successful offline switches will
@@ -1001,7 +1001,7 @@
      *
      * <p>Note that for common usage scenarios like creating a new session or closing the camera
      * device, it is faster to call respective APIs directly (see below for more details) without
-     * calling into this method. This API is only useful when application wants to uncofigure the
+     * calling into this method. This API is only useful when application wants to unconfigure the
      * camera but keep the device open for later use.</p>
      *
      * <p>Creating a new capture session with {@link CameraDevice#createCaptureSession}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index dfb9cf6..0e4c3c0 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -651,7 +651,7 @@
      * @param metadataClass The subclass of CameraMetadata that you want to get the keys for.
      * @param keyClass The class of the metadata key, e.g. CaptureRequest.Key.class
      * @param filterTags An array of tags to be used for filtering
-     * @param includeSynthetic Include public syntethic tag by default.
+     * @param includeSynthetic Include public synthetic tag by default.
      *
      * @return List of keys supported by this CameraDevice for metadataClass.
      *
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 42aa608..5feda78 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -141,7 +141,7 @@
      * parameters. All automatic control is disabled (auto-exposure, auto-white
      * balance, auto-focus), and post-processing parameters are set to preview
      * quality. The manual capture parameters (exposure, sensitivity, and so on)
-     * are set to reasonable defaults, but should be overriden by the
+     * are set to reasonable defaults, but should be overridden by the
      * application depending on the intended use case.
      * This template is guaranteed to be supported on camera devices that support the
      * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}
@@ -680,7 +680,7 @@
      * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution GPU processing with preview.</td> </tr>
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution in-app processing with preview.</td> </tr>
-     * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution two-input in-app processsing.</td> </tr>
+     * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution two-input in-app processing.</td> </tr>
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>Video recording with maximum-size video snapshot</td> </tr>
      * <tr> <td>{@code YUV }</td><td id="rb">{@code 640x480}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Standard video recording plus maximum-resolution in-app processing.</td> </tr>
      * <tr> <td>{@code YUV }</td><td id="rb">{@code 640x480}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Preview plus two-input maximum-resolution in-app processing.</td> </tr>
@@ -722,7 +722,7 @@
      * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution GPU processing with preview.</td> </tr>
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution in-app processing with preview.</td> </tr>
-     * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input in-app processsing.</td> </tr>
+     * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input in-app processing.</td> </tr>
      * </table><br>
      * </p>
      *
@@ -1137,7 +1137,7 @@
      * <tr><th colspan="13">Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors (YUV / PRIV inputs are guaranteed only if YUV / PRIVATE reprocessing are supported)</th></tr>
      * <tr> <th colspan="3" id="rb">Input</th> <th colspan="3" id="rb">Target 1</th> <th colspan="3" id="rb">Target 2</th>  <th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
      * <tr> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th><th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th></tr>
-     * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>RAW remosaic reprocessing with seperate preview</td> </tr>
+     * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>RAW remosaic reprocessing with separate preview</td> </tr>
      * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG / YUV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td>Ultra high res RAW -> JPEG / YUV with seperate preview</td> </tr>
      * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code YUV / PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG }</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td> Ultra high res PRIV / YUV -> YUV / JPEG reprocessing with seperate preview</td> </tr>
      * </table><br>
@@ -1260,7 +1260,7 @@
      * settings by calling {@link CaptureRequest.Builder#setPhysicalCameraKey}.</p>
      *
      * <p>Individual physical camera settings will only be honored for camera session
-     * that was initialiazed with corresponding physical camera id output configuration
+     * that was initialized with corresponding physical camera id output configuration
      * {@link OutputConfiguration#setPhysicalCameraId} and the same output targets are
      * also attached in the request by {@link CaptureRequest.Builder#addTarget}.</p>
      *
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 696873f..144b1de 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -256,7 +256,7 @@
 
     /**
      * Similar to getCameraIdList(). However, getCamerIdListNoLazy() necessarily communicates with
-     * cameraserver in order to get the list of camera ids. This is to faciliate testing since some
+     * cameraserver in order to get the list of camera ids. This is to facilitate testing since some
      * camera ids may go 'offline' without callbacks from cameraserver because of changes in
      * SYSTEM_CAMERA permissions (though this is not a changeable permission, tests may call
      * adopt(drop)ShellPermissionIdentity() and effectively change their permissions). This call
@@ -560,7 +560,7 @@
         }
 
         // Query the characteristics of all physical sub-cameras, and combine the multi-resolution
-        // stream configurations. Alternatively, for ultra-high resolution camera, direclty use
+        // stream configurations. Alternatively, for ultra-high resolution camera, directly use
         // its multi-resolution stream configurations. Note that framework derived formats such as
         // HEIC and DEPTH_JPEG aren't supported as multi-resolution input or output formats.
         Set<String> physicalCameraIds = info.getPhysicalCameraIds();
@@ -835,7 +835,7 @@
      * Opening the same camera ID twice in the same application will similarly cause the
      * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback
      * being fired for the {@link CameraDevice} from the first open call and all ongoing tasks
-     * being droppped.</p>
+     * being dropped.</p>
      *
      * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
      * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
@@ -1759,7 +1759,7 @@
         private final Set<Set<String>> mConcurrentCameraIdCombinations =
                 new ArraySet<Set<String>>();
 
-        // Registered availablility callbacks and their executors
+        // Registered availability callbacks and their executors
         private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
             new ArrayMap<AvailabilityCallback, Executor>();
 
@@ -2846,7 +2846,7 @@
                 // Tell listeners that the cameras and torch modes are unavailable and schedule a
                 // reconnection to camera service. When camera service is reconnected, the camera
                 // and torch statuses will be updated.
-                // Iterate from the end to the beginning befcause onStatusChangedLocked removes
+                // Iterate from the end to the beginning because onStatusChangedLocked removes
                 // entries from the ArrayMap.
                 for (int i = mDeviceStatus.size() - 1; i >= 0; i--) {
                     String cameraId = mDeviceStatus.keyAt(i);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ea99847..a7e28e2 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -159,7 +159,7 @@
      * Optionally, if {@code filterTags} is not {@code null}, then filter out any keys
      * whose native {@code tag} is not in {@code filterTags}. The {@code filterTags} array will be
      * sorted as a side effect.
-     * {@code includeSynthetic} Includes public syntenthic fields by default.
+     * {@code includeSynthetic} Includes public synthetic fields by default.
      * </p>
      */
      /*package*/ @SuppressWarnings("unchecked")
@@ -2308,7 +2308,7 @@
     /**
      * <p>An external flash has been turned on.</p>
      * <p>It informs the camera device that an external flash has been turned on, and that
-     * metering (and continuous focus if active) should be quickly recaculated to account
+     * metering (and continuous focus if active) should be quickly recalculated to account
      * for the external flash. Otherwise, this mode acts like ON.</p>
      * <p>When the external flash is turned off, AE mode should be changed to one of the
      * other available AE modes.</p>
diff --git a/core/java/android/hardware/camera2/CameraOfflineSession.java b/core/java/android/hardware/camera2/CameraOfflineSession.java
index 312559c..c219886 100644
--- a/core/java/android/hardware/camera2/CameraOfflineSession.java
+++ b/core/java/android/hardware/camera2/CameraOfflineSession.java
@@ -152,7 +152,7 @@
      *
      * <p>Closing a session is idempotent; closing more than once has no effect.</p>
      *
-     * @throws IllegalStateException if the offline sesion is not ready.
+     * @throws IllegalStateException if the offline session is not ready.
      */
     @Override
     public abstract void close();
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index cc484ea..1ae2fe1 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -482,7 +482,7 @@
     }
 
     private static final int DEFAULT_PIXEL_STRIDE = 2; // bytes per sample
-    private static final int BYTES_PER_RGB_PIX = 3; // byts per pixel
+    private static final int BYTES_PER_RGB_PIX = 3; // bytes per pixel
 
     // TIFF tag values needed to map between public API and TIFF spec
     private static final int TAG_ORIENTATION_UNKNOWN = 9;
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
index 34d016a..7c54a9b 100644
--- a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
+++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
@@ -36,4 +36,5 @@
     int surfaceGroupId;
     String physicalCameraId;
     List<CameraOutputConfig> sharedSurfaceConfigs;
+    boolean isMultiResolutionOutput;
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 2fa8b87..cfade55 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -258,6 +258,9 @@
             OutputConfiguration cameraOutput = new OutputConfiguration(output.surfaceGroupId,
                     outputSurface);
 
+            if (output.isMultiResolutionOutput) {
+                cameraOutput.setMultiResolutionOutput();
+            }
             if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) {
                 cameraOutput.enableSurfaceSharing();
                 for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) {
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 86c453b..0a4a1f0 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -607,7 +607,7 @@
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
-                "Maximum-resolution two-input in-app processsing"),
+                "Maximum-resolution two-input in-app processing"),
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
@@ -891,7 +891,7 @@
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
-                "Standard stil image capture"),
+                "Standard still image capture"),
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 857f62d..21540bf 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -165,7 +165,7 @@
      * device runs in fixed frame rate. The timestamp is roughly in the same time base as
      * {@link android.os.SystemClock#uptimeMillis}.</li>
      * <li> For an output surface of MediaRecorder, MediaCodec, or ImageReader with {@link
-     * android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE} usge flag, the timestamp base is
+     * android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE} usage flag, the timestamp base is
      * {@link #TIMESTAMP_BASE_MONOTONIC}, which is roughly the same time base as
      * {@link android.os.SystemClock#uptimeMillis}.</li>
      * <li> For all other cases, the timestamp base is {@link #TIMESTAMP_BASE_SENSOR}, the same
@@ -418,7 +418,7 @@
      *         call, or no non-negative group ID has been set.
      * @hide
      */
-    void setMultiResolutionOutput() {
+    public void setMultiResolutionOutput() {
         if (mIsShared) {
             throw new IllegalStateException("Multi-resolution output flag must not be set for " +
                     "configuration with surface sharing");
@@ -654,7 +654,7 @@
             mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
         } else {
             mSurfaceType = SURFACE_TYPE_UNKNOWN;
-            throw new IllegalArgumentException("Unknow surface source class type");
+            throw new IllegalArgumentException("Unknown surface source class type");
         }
 
         if (surfaceSize.getWidth() == 0 || surfaceSize.getHeight() == 0) {
@@ -715,7 +715,7 @@
      * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
      * MediaRecorder, MediaCodec, or implementation defined ImageReader.</p>
      *
-     * <p>This function must not be called from OuptutConfigurations created by {@link
+     * <p>This function must not be called from OutputConfigurations created by {@link
      * #createInstancesForMultiResolutionOutput}.</p>
      *
      * @throws IllegalStateException If this OutputConfiguration is created via {@link
@@ -934,7 +934,7 @@
      *
      * <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
      *  OutputConfiguration. The only notable exception is the surface associated with
-     *  the OutputConfigration see {@link #getSurface} which was passed as part of the constructor
+     *  the OutputConfiguration see {@link #getSurface} which was passed as part of the constructor
      *  or was added first in the deferred case
      *  {@link OutputConfiguration#OutputConfiguration(Size, Class)}.</p>
      *
@@ -962,7 +962,7 @@
      * for scenarios where the immediate consumer target isn't sufficient to indicate the stream's
      * usage.</p>
      *
-     * <p>The main difference beteween stream use case and capture intent is that the former
+     * <p>The main difference between stream use case and capture intent is that the former
      * enables the camera device to optimize camera hardware and software pipelines based on user
      * scenarios for each stream, whereas the latter is mainly a hint to camera to decide
      * optimal 3A strategy that's applicable to the whole session. The camera device carries out
@@ -1123,7 +1123,7 @@
      * CameraCharacteristics#SENSOR_READOUT_TIMESTAMP} is
      * {@link CameraMetadata#SENSOR_READOUT_TIMESTAMP_HARDWARE}.</p>
      *
-     * <p>As long as readout timestamp is supported, if the timestamp base isi
+     * <p>As long as readout timestamp is supported, if the timestamp base is
      * {@link #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED}, or if the timestamp base is DEFAULT for a
      * SurfaceView output, the image timestamps for the output are always readout time regardless
      * of whether this function is called.</p>
@@ -1420,9 +1420,9 @@
      */
     @Override
     public int hashCode() {
-        // Need ensure that the hashcode remains unchanged after adding a deferred surface. Otherwise
-        // the deferred output configuration will be lost in the camera streammap after the deferred
-        // surface is set.
+        // Need ensure that the hashcode remains unchanged after adding a deferred surface.
+        // Otherwise the deferred output configuration will be lost in the camera stream map
+        // after the deferred surface is set.
         if (mIsDeferredConfig) {
             return HashCodeHelpers.hashCode(
                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
@@ -1446,7 +1446,7 @@
     private static final String TAG = "OutputConfiguration";
 
     // A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
-    // incremented everytime {@link createInstancesForMultiResolutionOutput} is called.
+    // incremented every time {@link createInstancesForMultiResolutionOutput} is called.
     private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0;
 
     private ArrayList<Surface> mSurfaces;
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 5a48176..aabe149 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1790,7 +1790,7 @@
      *
      * <p>{@code ValidOutputFormatsForInput([in:%s(%d), out:%s(%d), ... %s(%d)],
      * ... [in:%s(%d), out:%s(%d), ... %s(%d)])}, where {@code [in:%s(%d), out:%s(%d), ... %s(%d)]}
-     * represents an input fomat and its valid output formats.</p>
+     * represents an input format and its valid output formats.</p>
      *
      * <p>{@code HighSpeedVideoConfigurations([w:%d, h:%d, min_fps:%d, max_fps:%d],
      * ... [w:%d, h:%d, min_fps:%d, max_fps:%d])}, where
diff --git a/core/java/android/hardware/display/HdrConversionMode.java b/core/java/android/hardware/display/HdrConversionMode.java
index 49e5eff..5fccb5e 100644
--- a/core/java/android/hardware/display/HdrConversionMode.java
+++ b/core/java/android/hardware/display/HdrConversionMode.java
@@ -29,9 +29,6 @@
 /**
  * Describes the HDR conversion mode for a device.
  *
- * This class is used when user changes the HDR conversion mode of the device via
- * {@link DisplayManager#setHdrConversionMode(HdrConversionMode)}.
- * <p>
  * HDR conversion mode has a conversionMode and preferredHdrOutputType. </p><p>
  * The conversionMode can be one of:
  * {@link HdrConversionMode#HDR_CONVERSION_UNSUPPORTED} : HDR conversion is unsupported. In this
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 490e55b..3fffb3c 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -438,7 +438,7 @@
          *
          * <p>For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on
          * a 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded up
-         * down to a divisor of the physical display. If unset or zero, the virtual display will be
+         * to a divisor of the physical display. If unset or zero, the virtual display will be
          * refreshed at the physical display refresh rate.
          *
          * @see Display#getRefreshRate()
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 089b159..0ba0c5a 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -88,6 +88,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void setTimeout(int timeout) {
         try {
@@ -106,6 +107,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public int getTimeout() {
         try {
@@ -167,6 +169,7 @@
      * @return response bytes received, will not be null
      * @throws TagLostException if the tag leaves the field
      * @throws IOException if there is an I/O failure, or this operation is canceled
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public byte[] transceive(byte[] data) throws IOException {
         return transceive(data, true);
@@ -193,6 +196,7 @@
      * support.
      *
      * @return whether the NFC adapter on this device supports extended length APDUs.
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public boolean isExtendedLengthApduSupported() {
         try {
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index 080e058..26f54e6 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -597,6 +597,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void setTimeout(int timeout) {
         try {
@@ -615,6 +616,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public int getTimeout() {
         try {
diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java
index dec2c65..c0416a3 100644
--- a/core/java/android/nfc/tech/MifareUltralight.java
+++ b/core/java/android/nfc/tech/MifareUltralight.java
@@ -236,6 +236,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void setTimeout(int timeout) {
         try {
@@ -255,6 +256,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public int getTimeout() {
         try {
diff --git a/core/java/android/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java
index 39c355a..7d83f15 100644
--- a/core/java/android/nfc/tech/Ndef.java
+++ b/core/java/android/nfc/tech/Ndef.java
@@ -261,6 +261,7 @@
      * @throws TagLostException if the tag leaves the field
      * @throws IOException if there is an I/O failure, or the operation is canceled
      * @throws FormatException if the NDEF Message on the tag is malformed
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public NdefMessage getNdefMessage() throws IOException, FormatException {
         checkConnected();
@@ -301,6 +302,7 @@
      * @throws TagLostException if the tag leaves the field
      * @throws IOException if there is an I/O failure, or the operation is canceled
      * @throws FormatException if the NDEF Message to write is malformed
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
         checkConnected();
@@ -339,6 +341,7 @@
      * <p>Does not cause any RF activity and does not block.
      *
      * @return true if it is possible to make this tag read-only
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public boolean canMakeReadOnly() {
         INfcTag tagService = mTag.getTagService();
@@ -370,6 +373,7 @@
      * @return true on success, false if it is not possible to make this tag read-only
      * @throws TagLostException if the tag leaves the field
      * @throws IOException if there is an I/O failure, or the operation is canceled
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public boolean makeReadOnly() throws IOException {
         checkConnected();
diff --git a/core/java/android/nfc/tech/NdefFormatable.java b/core/java/android/nfc/tech/NdefFormatable.java
index 4175cd0..f19d302 100644
--- a/core/java/android/nfc/tech/NdefFormatable.java
+++ b/core/java/android/nfc/tech/NdefFormatable.java
@@ -111,6 +111,7 @@
      * @throws TagLostException if the tag leaves the field
      * @throws IOException if there is an I/O failure, or the operation is canceled
      * @throws FormatException if the NDEF Message to write is malformed
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void formatReadOnly(NdefMessage firstMessage) throws IOException, FormatException {
         format(firstMessage, true);
diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java
index 88730f9..7e66483 100644
--- a/core/java/android/nfc/tech/NfcA.java
+++ b/core/java/android/nfc/tech/NfcA.java
@@ -141,6 +141,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void setTimeout(int timeout) {
         try {
@@ -159,6 +160,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public int getTimeout() {
         try {
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index 4487121..2ccd388 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -145,6 +145,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param timeout timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void setTimeout(int timeout) {
         try {
@@ -163,6 +164,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @return timeout value in milliseconds
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public int getTimeout() {
         try {
diff --git a/core/java/android/nfc/tech/TagTechnology.java b/core/java/android/nfc/tech/TagTechnology.java
index 0e2c7c1..839fe42 100644
--- a/core/java/android/nfc/tech/TagTechnology.java
+++ b/core/java/android/nfc/tech/TagTechnology.java
@@ -176,6 +176,7 @@
      * @see #close()
      * @throws TagLostException if the tag leaves the field
      * @throws IOException if there is an I/O failure, or connect is canceled
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void connect() throws IOException;
 
@@ -193,6 +194,7 @@
      * @see #close()
      * @throws TagLostException if the tag leaves the field
      * @throws IOException if there is an I/O failure, or connect is canceled
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      * @hide
      */
     public void reconnect() throws IOException;
@@ -205,6 +207,7 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @see #connect()
+     * @throws SecurityException if the tag object is reused after the tag has left the field
      */
     public void close() throws IOException;
 
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index b478a379..af09a06 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1295,19 +1295,31 @@
      * value, such as 256MB or 32GB. This avoids showing weird values like
      * "29.5GB" in UI.
      *
+     * Some storage devices are still using GiB (powers of 1024) over
+     * GB (powers of 1000) measurements and this method takes it into account.
+     *
+     * Round ranges:
+     * ...
+     * [256 GiB + 1; 512 GiB] -> 512 GB
+     * [512 GiB + 1; 1 TiB]   -> 1 TB
+     * [1 TiB + 1; 2 TiB]     -> 2 TB
+     * etc
+     *
      * @hide
      */
     public static long roundStorageSize(long size) {
         long val = 1;
-        long pow = 1;
-        while ((val * pow) < size) {
+        long kiloPow = 1;
+        long kibiPow = 1;
+        while ((val * kibiPow) < size) {
             val <<= 1;
             if (val > 512) {
                 val = 1;
-                pow *= 1000;
+                kibiPow *= 1024;
+                kiloPow *= 1000;
             }
         }
-        return val * pow;
+        return val * kiloPow;
     }
 
     private static long toBytes(long value, String unit) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1673ade..e784c26 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4499,17 +4499,28 @@
         public void writeToParcel(Parcel out) {
             Parcel source = mSource;
             if (source != null) {
-                out.appendFrom(source, mPosition, mLength);
-            } else {
-                out.writeValue(mObject);
+                synchronized (source) {
+                    if (mSource != null) {
+                        out.appendFrom(source, mPosition, mLength);
+                        return;
+                    }
+                }
             }
+
+            out.writeValue(mObject);
         }
 
         public boolean hasFileDescriptors() {
             Parcel source = mSource;
-            return (source != null)
-                    ? source.hasFileDescriptors(mPosition, mLength)
-                    : Parcel.hasFileDescriptors(mObject);
+            if (source != null) {
+                synchronized (source) {
+                    if (mSource != null) {
+                        return source.hasFileDescriptors(mPosition, mLength);
+                    }
+                }
+            }
+
+            return Parcel.hasFileDescriptors(mObject);
         }
 
         @Override
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 123f480..89b768d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14777,23 +14777,6 @@
                 "adaptive_battery_management_enabled";
 
         /**
-         * Whether or not apps are allowed into the
-         * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
-         * Type: int (0 for false, 1 for true)
-         * Default: {@value #DEFAULT_ENABLE_RESTRICTED_BUCKET}
-         *
-         * @hide
-         */
-        @Readable
-        public static final String ENABLE_RESTRICTED_BUCKET = "enable_restricted_bucket";
-
-        /**
-         * @see #ENABLE_RESTRICTED_BUCKET
-         * @hide
-         */
-        public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 1;
-
-        /**
          * Whether or not app auto restriction is enabled. When it is enabled, settings app will
          * auto restrict the app if it has bad behavior (e.g. hold wakelock for long time).
          *
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 3190c69..ea86fcc 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -33,17 +33,28 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.credentials.CredentialManager;
 import android.credentials.CredentialProviderInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
+import android.util.Xml;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -58,6 +69,11 @@
 public final class CredentialProviderInfoFactory {
     private static final String TAG = "CredentialProviderInfoFactory";
 
+    private static final String TAG_CREDENTIAL_PROVIDER = "credential-provider";
+    private static final String TAG_CAPABILITIES = "capabilities";
+    private static final String TAG_CAPABILITY = "capability";
+    private static final String ATTR_NAME = "name";
+
     /**
      * Constructs an information instance of the credential provider.
      *
@@ -118,8 +134,8 @@
     }
 
     /**
-     * Constructs an information instance of the credential provider for testing purposes. Does
-     * not run any verifications and passes parameters as is.
+     * Constructs an information instance of the credential provider for testing purposes. Does not
+     * run any verifications and passes parameters as is.
      */
     @VisibleForTesting
     public static CredentialProviderInfo createForTests(
@@ -134,7 +150,6 @@
                 .setSystemProvider(isSystemProvider)
                 .addCapabilities(capabilities)
                 .build();
-
     }
 
     private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException {
@@ -194,10 +209,8 @@
     private static CredentialProviderInfo.Builder populateMetadata(
             @NonNull Context context, ServiceInfo serviceInfo) {
         requireNonNull(context, "context must not be null");
-
-        final CredentialProviderInfo.Builder builder =
-                new CredentialProviderInfo.Builder(serviceInfo);
         final PackageManager pm = context.getPackageManager();
+        CredentialProviderInfo.Builder builder = new CredentialProviderInfo.Builder(serviceInfo);
 
         // 1. Get the metadata for the service.
         final Bundle metadata = serviceInfo.metaData;
@@ -206,46 +219,165 @@
             return builder;
         }
 
-        // 2. Extract the capabilities from the bundle.
+        // 2. Get the resources for the application.
+        Resources resources = null;
         try {
-            Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
-            if (metadata == null || resources == null) {
-                Log.i(TAG, "populateMetadata - resources is null");
-                return builder;
-            }
-
-            builder.addCapabilities(populateProviderCapabilities(resources, metadata, serviceInfo));
+            resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
         } catch (PackageManager.NameNotFoundException e) {
-            Slog.e(TAG, e.getMessage());
+            Log.e(TAG, "Failed to get app resources", e);
+        }
+
+        // 3. Stop if we are missing data.
+        if (metadata == null || resources == null) {
+            Log.i(TAG, "populateMetadata - resources is null");
+            return builder;
+        }
+
+        // 4. Extract the XML metadata.
+        try {
+            builder = extractXmlMetadata(context, builder, serviceInfo, pm, resources);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to get XML metadata", e);
+        }
+
+        // 5. Extract the legacy metadata.
+        try {
+            builder.addCapabilities(
+                    populateLegacyProviderCapabilities(resources, metadata, serviceInfo));
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to get legacy metadata ", e);
         }
 
         return builder;
     }
 
-    private static List<String> populateProviderCapabilities(
-            Resources resources, Bundle metadata, ServiceInfo serviceInfo) {
-        List<String> output = new ArrayList<>();
-        String[] capabilities = new String[0];
-
-        try {
-            capabilities =
-                    resources.getStringArray(
-                            metadata.getInt(CredentialProviderService.CAPABILITY_META_DATA_KEY));
-        } catch (Resources.NotFoundException e) {
-            Slog.e(TAG, "Failed to get capabilities: " + e.getMessage());
+    private static CredentialProviderInfo.Builder extractXmlMetadata(
+            @NonNull Context context,
+            @NonNull CredentialProviderInfo.Builder builder,
+            @NonNull ServiceInfo serviceInfo,
+            @NonNull PackageManager pm,
+            @NonNull Resources resources) {
+        final XmlResourceParser parser =
+                serviceInfo.loadXmlMetaData(pm, CredentialProviderService.SERVICE_META_DATA);
+        if (parser == null) {
+            return builder;
         }
 
-        if (capabilities == null || capabilities.length == 0) {
-            Slog.e(TAG, "No capabilities found for provider:" + serviceInfo.packageName);
+        try {
+            int type = 0;
+            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+                type = parser.next();
+            }
+
+            // This is matching a <credential-provider /> tag in the XML.
+            if (TAG_CREDENTIAL_PROVIDER.equals(parser.getName())) {
+                final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+                TypedArray afsAttributes = null;
+                try {
+                    afsAttributes =
+                            resources.obtainAttributes(
+                                    allAttributes,
+                                    com.android.internal.R.styleable.CredentialProvider);
+                    builder.setSettingsSubtitle(
+                            afsAttributes.getString(
+                                    R.styleable.CredentialProvider_settingsSubtitle));
+                } catch (Exception e) {
+                    Log.e(TAG, "Failed to get XML attr", e);
+                } finally {
+                    if (afsAttributes != null) {
+                        afsAttributes.recycle();
+                    }
+                }
+                builder.addCapabilities(parseXmlProviderOuterCapabilities(parser, resources));
+            } else {
+                Log.e(TAG, "Meta-data does not start with credential-provider-service tag");
+            }
+        } catch (IOException | XmlPullParserException e) {
+            Log.e(TAG, "Error parsing credential provider service meta-data", e);
+        }
+
+        return builder;
+    }
+
+    private static Set<String> parseXmlProviderOuterCapabilities(
+            XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException {
+        final Set<String> capabilities = new HashSet<>();
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (TAG_CAPABILITIES.equals(parser.getName())) {
+                capabilities.addAll(parseXmlProviderInnerCapabilities(parser, resources));
+            }
+        }
+
+        return capabilities;
+    }
+
+    private static List<String> parseXmlProviderInnerCapabilities(
+            XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException {
+        List<String> capabilities = new ArrayList<>();
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (TAG_CAPABILITY.equals(parser.getName())) {
+                String name = parser.getAttributeValue(null, ATTR_NAME);
+                if (name != null && !TextUtils.isEmpty(name)) {
+                    capabilities.add(name);
+                }
+            }
+        }
+
+        return capabilities;
+    }
+
+    private static Set<String> populateLegacyProviderCapabilities(
+            Resources resources, Bundle metadata, ServiceInfo serviceInfo) {
+        Set<String> output = new HashSet<>();
+        Set<String> capabilities = new HashSet<>();
+
+        try {
+            String[] discovered =
+                    resources.getStringArray(
+                            metadata.getInt(CredentialProviderService.CAPABILITY_META_DATA_KEY));
+            if (discovered != null) {
+                capabilities.addAll(Arrays.asList(discovered));
+            }
+        } catch (Resources.NotFoundException | NullPointerException e) {
+            Log.e(TAG, "Failed to get capabilities: ", e);
+        }
+
+        try {
+            String[] discovered =
+                    metadata.getStringArray(CredentialProviderService.CAPABILITY_META_DATA_KEY);
+            if (discovered != null) {
+                capabilities.addAll(Arrays.asList(discovered));
+            }
+        } catch (Resources.NotFoundException | NullPointerException e) {
+            Log.e(TAG, "Failed to get capabilities: ", e);
+        }
+
+        if (capabilities.size() == 0) {
+            Log.e(TAG, "No capabilities found for provider:" + serviceInfo);
             return output;
         }
 
         for (String capability : capabilities) {
-            if (capability.isEmpty()) {
-                Slog.e(TAG, "Skipping empty capability");
+            if (capability == null || capability.isEmpty()) {
+                Log.w(TAG, "Skipping empty/null capability");
                 continue;
             }
-            Slog.e(TAG, "Capabilities found for provider: " + capability);
+            Log.i(TAG, "Capabilities found for provider: " + capability);
             output.add(capability);
         }
         return output;
@@ -361,7 +493,8 @@
 
         try {
             DevicePolicyManager dpm = newContext.getSystemService(DevicePolicyManager.class);
-            return dpm.getCredentialManagerPolicy();
+            PackagePolicy pp = dpm.getCredentialManagerPolicy();
+            return pp;
         } catch (SecurityException e) {
             // If the current user is not enrolled in DPM then this can throw a security error.
             Log.e(TAG, "Failed to get device policy: " + e);
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index e88474d..6824159 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -156,8 +156,43 @@
 
     private static final String TAG = "CredProviderService";
 
+    /**
+     * The list of capabilities exposed by a credential provider.
+     *
+     * @deprecated Replaced with {@link android.service.credentials#SERVICE_META_DATA}
+     */
+    @Deprecated
     public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
 
+     /**
+      * Name under which a Credential Provider service component publishes information
+      * about itself.  This meta-data must reference an XML resource containing
+      * an
+      * <code>&lt;{@link android.R.styleable#CredentialProvider credential-provider}&gt;</code>
+      * tag.
+      *
+      * For example (AndroidManifest.xml):
+      * <code>
+      * <meta-data
+      *         android:name="android.credentials.provider"
+      *          android:resource="@xml/provider"/>
+      * </code>
+      *
+      * For example (xml/provider.xml):
+      * <code>
+      * <credential-provider xmlns:android="http://schemas.android.com/apk/res/android"
+      *       android:settingsSubtitle="@string/providerSubtitle">
+      *      <capabilities>
+      *          <capability>@string/passwords</capability>
+      *          <capability>@string/passkeys</capability>
+      *      </capabilities>
+      *      <string name="passwords">android.credentials.TYPE_PASSWORD_CREDENTIAL</string>
+      *      <string name="passkeys">android.credentials.TYPE_PUBLIC_KEY_CREDENTIAL</string>
+      *  </credential-provider>
+      * </code>
+      */
+    public static final String SERVICE_META_DATA = "android.credentials.provider";
+
     /** @hide */
     public static final String TEST_SYSTEM_PROVIDER_META_DATA_KEY =
             "android.credentials.testsystemprovider";
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 8d95c02..0b947fc 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -60,6 +60,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -97,6 +98,7 @@
 import android.window.ClientWindowFrames;
 import android.window.ScreenCapture;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.view.BaseIWindow;
@@ -104,9 +106,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
@@ -167,11 +170,12 @@
     private static final int MSG_REPORT_SHOWN = 10150;
     private static final int MSG_UPDATE_DIMMING = 10200;
     private static final int MSG_WALLPAPER_FLAGS_CHANGED = 10210;
-    private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY,
-            Float.NEGATIVE_INFINITY);
 
+    /** limit calls to {@link Engine#onComputeColors} to at most once per second */
     private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
-    private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 1000;
+
+    /** limit calls to {@link Engine#processLocalColorsInternal} to at most once per 2 seconds */
+    private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 2000;
 
     private static final boolean ENABLE_WALLPAPER_DIMMING =
             SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true);
@@ -180,6 +184,9 @@
 
     private final ArrayMap<IBinder, IWallpaperEngineWrapper> mActiveEngines = new ArrayMap<>();
 
+    private Handler mBackgroundHandler;
+    private HandlerThread mBackgroundThread;
+
     static final class WallpaperCommand {
         String action;
         int x;
@@ -198,14 +205,6 @@
      */
     public class Engine {
         IWallpaperEngineWrapper mIWallpaperEngine;
-        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
-        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
-
-        // 2D matrix [x][y] to represent a page of a portion of a window
-        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
-        Bitmap mLastScreenshot;
-        int mLastWindowPage = -1;
-        private boolean mResetWindowPages;
 
         // Copies from mIWallpaperEngine.
         HandlerCaller mCaller;
@@ -266,11 +265,34 @@
 
         final Object mLock = new Object();
         boolean mOffsetMessageEnqueued;
+
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-        float mPendingXOffset;
-        float mPendingYOffset;
-        float mPendingXOffsetStep;
-        float mPendingYOffsetStep;
+        @GuardedBy("mLock")
+        private float mPendingXOffset;
+        @GuardedBy("mLock")
+        private float mPendingYOffset;
+        @GuardedBy("mLock")
+        private float mPendingXOffsetStep;
+        @GuardedBy("mLock")
+        private float mPendingYOffsetStep;
+
+        /**
+         * local color extraction related fields. When a user calls `addLocalColorAreas`
+         */
+        @GuardedBy("mLock")
+        private final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
+
+        @GuardedBy("mLock")
+        private final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
+        private long mLastProcessLocalColorsTimestamp;
+        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
+        private int mPixelCopyCount = 0;
+        // 2D matrix [x][y] to represent a page of a portion of a window
+        @GuardedBy("mLock")
+        private EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
+        private Bitmap mLastScreenshot;
+        private boolean mResetWindowPages;
+
         boolean mPendingSync;
         MotionEvent mPendingMove;
         boolean mIsInAmbientMode;
@@ -279,12 +301,8 @@
         private long mLastColorInvalidation;
         private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
 
-        // used to throttle processLocalColors
-        private long mLastProcessLocalColorsTimestamp;
-        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
-
         private Display mDisplay;
         private Context mDisplayContext;
         private int mDisplayState;
@@ -854,7 +872,7 @@
                             + "was not established.");
                 }
                 mResetWindowPages = true;
-                processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+                processLocalColors();
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
             }
@@ -1389,10 +1407,9 @@
                         mIsCreating = false;
                         mSurfaceCreated = true;
                         if (redrawNeeded) {
-                            resetWindowPages();
                             mSession.finishDrawing(mWindow, null /* postDrawTransaction */,
                                                    Integer.MAX_VALUE);
-                            processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+                            processLocalColors();
                         }
                         reposition();
                         reportEngineShown(shouldWaitForEngineShown());
@@ -1536,7 +1553,7 @@
             if (!mDestroyed) {
                 mVisible = visible;
                 reportVisibility(false);
-                if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+                if (mReportedVisible) processLocalColors();
             } else {
                 AnimationHandler.requestAnimatorsEnabled(visible, this);
             }
@@ -1640,14 +1657,14 @@
             }
 
             // setup local color extraction data
-            processLocalColors(xOffset, xOffsetStep);
+            processLocalColors();
         }
 
         /**
          * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of
          * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls.
          */
-        private void processLocalColors(float xOffset, float xOffsetStep) {
+        private void processLocalColors() {
             if (mProcessLocalColorsPending.compareAndSet(false, true)) {
                 final long now = mClockFunction.get();
                 final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp;
@@ -1657,80 +1674,98 @@
                 mHandler.postDelayed(() -> {
                     mLastProcessLocalColorsTimestamp = now + timeToWait;
                     mProcessLocalColorsPending.set(false);
-                    processLocalColorsInternal(xOffset, xOffsetStep);
+                    processLocalColorsInternal();
                 }, timeToWait);
             }
         }
 
-        private void processLocalColorsInternal(float xOffset, float xOffsetStep) {
-            // implemented by the wallpaper
+        /**
+         * Default implementation of the local color extraction.
+         * This will take a screenshot of the whole wallpaper on the main thread.
+         * Then, in a background thread, for each launcher page, for each area that needs color
+         * extraction in this page, creates a sub-bitmap and call {@link WallpaperColors#fromBitmap}
+         * to extract the colors. Every time a launcher page has been processed, call
+         * {@link #notifyLocalColorsChanged} with the color and areas of this page.
+         */
+        private void processLocalColorsInternal() {
             if (supportsLocalColorExtraction()) return;
-            if (DEBUG) {
-                Log.d(TAG, "processLocalColors " + xOffset + " of step "
-                        + xOffsetStep);
-            }
-            //below is the default implementation
-            if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN
-                    || !mSurfaceHolder.getSurface().isValid()) return;
-            int xCurrentPage;
+            float xOffset;
+            float xOffsetStep;
+            float wallpaperDimAmount;
+            int xPage;
             int xPages;
-            if (!validStep(xOffsetStep)) {
-                if (DEBUG) {
-                    Log.w(TAG, "invalid offset step " + xOffsetStep);
-                }
-                xOffset = 0;
-                xOffsetStep = 1;
-                xCurrentPage = 0;
-                xPages = 1;
-            } else {
-                xPages = Math.round(1 / xOffsetStep) + 1;
-                xOffsetStep = (float) 1 / (float) xPages;
-                float shrink = (float) (xPages - 1) / (float) xPages;
-                xOffset *= shrink;
-                xCurrentPage = Math.round(xOffset / xOffsetStep);
-            }
-            if (DEBUG) {
-                Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage);
-                Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset);
-            }
-
-            float finalXOffsetStep = xOffsetStep;
-            float finalXOffset = xOffset;
-
-            Trace.beginSection("WallpaperService#processLocalColors");
-            resetWindowPages();
-            int xPage = xCurrentPage;
+            Set<RectF> areas;
             EngineWindowPage current;
-            if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) {
-                mWindowPages = new EngineWindowPage[xPages];
-                initWindowPages(mWindowPages, finalXOffsetStep);
-            }
-            if (mLocalColorsToAdd.size() != 0) {
-                for (RectF colorArea : mLocalColorsToAdd) {
-                    if (!isValid(colorArea)) continue;
-                    mLocalColorAreas.add(colorArea);
-                    int colorPage = getRectFPage(colorArea, finalXOffsetStep);
-                    EngineWindowPage currentPage = mWindowPages[colorPage];
-                    currentPage.setLastUpdateTime(0);
-                    currentPage.removeColor(colorArea);
-                }
-                mLocalColorsToAdd.clear();
-            }
-            if (xPage >= mWindowPages.length) {
+
+            synchronized (mLock) {
+                xOffset = mPendingXOffset;
+                xOffsetStep = mPendingXOffsetStep;
+                wallpaperDimAmount = mWallpaperDimAmount;
+
                 if (DEBUG) {
-                    Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage);
-                    Log.e(TAG, "error on page " + xPage + " out of " + xPages);
-                    Log.e(TAG,
-                            "error on xOffsetStep " + finalXOffsetStep
-                                    + " xOffset " + finalXOffset);
+                    Log.d(TAG, "processLocalColors " + xOffset + " of step "
+                            + xOffsetStep);
                 }
-                xPage = mWindowPages.length - 1;
+                if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN
+                        || !mSurfaceHolder.getSurface().isValid()) return;
+                int xCurrentPage;
+                if (!validStep(xOffsetStep)) {
+                    if (DEBUG) {
+                        Log.w(TAG, "invalid offset step " + xOffsetStep);
+                    }
+                    xOffset = 0;
+                    xOffsetStep = 1;
+                    xCurrentPage = 0;
+                    xPages = 1;
+                } else {
+                    xPages = Math.round(1 / xOffsetStep) + 1;
+                    xOffsetStep = (float) 1 / (float) xPages;
+                    float shrink = (float) (xPages - 1) / (float) xPages;
+                    xOffset *= shrink;
+                    xCurrentPage = Math.round(xOffset / xOffsetStep);
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage);
+                    Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset);
+                }
+
+                float finalXOffsetStep = xOffsetStep;
+                float finalXOffset = xOffset;
+
+                resetWindowPages();
+                xPage = xCurrentPage;
+                if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) {
+                    mWindowPages = new EngineWindowPage[xPages];
+                    initWindowPages(mWindowPages, finalXOffsetStep);
+                }
+                if (mLocalColorsToAdd.size() != 0) {
+                    for (RectF colorArea : mLocalColorsToAdd) {
+                        if (!isValid(colorArea)) continue;
+                        mLocalColorAreas.add(colorArea);
+                        int colorPage = getRectFPage(colorArea, finalXOffsetStep);
+                        EngineWindowPage currentPage = mWindowPages[colorPage];
+                        currentPage.setLastUpdateTime(0);
+                        currentPage.removeColor(colorArea);
+                    }
+                    mLocalColorsToAdd.clear();
+                }
+                if (xPage >= mWindowPages.length) {
+                    if (DEBUG) {
+                        Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage);
+                        Log.e(TAG, "error on page " + xPage + " out of " + xPages);
+                        Log.e(TAG,
+                                "error on xOffsetStep " + finalXOffsetStep
+                                        + " xOffset " + finalXOffset);
+                    }
+                    xPage = mWindowPages.length - 1;
+                }
+                current = mWindowPages[xPage];
+                areas = new HashSet<>(current.getAreas());
             }
-            current = mWindowPages[xPage];
-            updatePage(current, xPage, xPages, finalXOffsetStep);
-            Trace.endSection();
+            updatePage(current, areas, xPage, xPages, wallpaperDimAmount);
         }
 
+        @GuardedBy("mLock")
         private void initWindowPages(EngineWindowPage[] windowPages, float step) {
             for (int i = 0; i < windowPages.length; i++) {
                 windowPages[i] = new EngineWindowPage();
@@ -1747,16 +1782,16 @@
             }
         }
 
-        void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
-                float xOffsetStep) {
+        void updatePage(EngineWindowPage currentPage, Set<RectF> areas, int pageIndx, int numPages,
+                float wallpaperDimAmount) {
+
             // in case the clock is zero, we start with negative time
             long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION;
             long lapsed = current - currentPage.getLastUpdateTime();
             // Always update the page when the last update time is <= 0
             // This is important especially when the device first boots
-            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) {
-                return;
-            }
+            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return;
+
             Surface surface = mSurfaceHolder.getSurface();
             if (!surface.isValid()) return;
             boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
@@ -1769,43 +1804,59 @@
                 Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height);
                 return;
             }
+            final String pixelCopySectionName = "WallpaperService#pixelCopy";
+            final int pixelCopyCount = mPixelCopyCount++;
+            Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount);
             Bitmap screenShot = Bitmap.createBitmap(width, height,
                     Bitmap.Config.ARGB_8888);
             final Bitmap finalScreenShot = screenShot;
-            Trace.beginSection("WallpaperService#pixelCopy");
-            PixelCopy.request(surface, screenShot, (res) -> {
-                Trace.endSection();
-                if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
-                if (res != PixelCopy.SUCCESS) {
-                    Bitmap lastBitmap = currentPage.getBitmap();
-                    // assign the last bitmap taken for now
-                    currentPage.setBitmap(mLastScreenshot);
-                    Bitmap lastScreenshot = mLastScreenshot;
-                    if (lastScreenshot != null && !lastScreenshot.isRecycled()
-                            && !Objects.equals(lastBitmap, lastScreenshot)) {
-                        updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+            try {
+                // TODO(b/274427458) check if this can be done in the background.
+                PixelCopy.request(surface, screenShot, (res) -> {
+                    Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount);
+                    if (DEBUG) {
+                        Log.d(TAG, "result of pixel copy is: "
+                                + (res == PixelCopy.SUCCESS ? "SUCCESS" : "FAILURE"));
                     }
-                } else {
-                    mLastScreenshot = finalScreenShot;
-                    // going to hold this lock for a while
-                    currentPage.setBitmap(finalScreenShot);
-                    currentPage.setLastUpdateTime(current);
-                    updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
-                }
-            }, mHandler);
-
+                    if (res != PixelCopy.SUCCESS) {
+                        Bitmap lastBitmap = currentPage.getBitmap();
+                        // assign the last bitmap taken for now
+                        currentPage.setBitmap(mLastScreenshot);
+                        Bitmap lastScreenshot = mLastScreenshot;
+                        if (lastScreenshot != null && !Objects.equals(lastBitmap, lastScreenshot)) {
+                            updatePageColors(
+                                    currentPage, areas, pageIndx, numPages, wallpaperDimAmount);
+                        }
+                    } else {
+                        mLastScreenshot = finalScreenShot;
+                        currentPage.setBitmap(finalScreenShot);
+                        currentPage.setLastUpdateTime(current);
+                        updatePageColors(
+                                currentPage, areas, pageIndx, numPages, wallpaperDimAmount);
+                    }
+                }, mBackgroundHandler);
+            } catch (IllegalArgumentException e) {
+                // this can potentially happen if the surface is invalidated right between the
+                // surface.isValid() check and the PixelCopy operation.
+                // in this case, stop: we'll compute colors on the next processLocalColors call.
+                Log.w(TAG, "Cancelling processLocalColors: exception caught during PixelCopy");
+            }
         }
         // locked by the passed page
-        private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages,
-                float xOffsetStep) {
+        private void updatePageColors(EngineWindowPage page, Set<RectF> areas,
+                int pageIndx, int numPages, float wallpaperDimAmount) {
             if (page.getBitmap() == null) return;
+            if (!mBackgroundHandler.getLooper().isCurrentThread()) {
+                throw new IllegalStateException(
+                        "ProcessLocalColors should be called from the background thread");
+            }
             Trace.beginSection("WallpaperService#updatePageColors");
             if (DEBUG) {
                 Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas "
                         + page.getAreas().size() + " and bitmap size of "
                         + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight());
             }
-            for (RectF area: page.getAreas()) {
+            for (RectF area: areas) {
                 if (area == null) continue;
                 RectF subArea = generateSubRect(area, pageIndx, numPages);
                 Bitmap b = page.getBitmap();
@@ -1815,12 +1866,12 @@
                 int height = Math.round(b.getHeight() * subArea.height());
                 Bitmap target;
                 try {
-                    target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height);
+                    target = Bitmap.createBitmap(b, x, y, width, height);
                 } catch (Exception e) {
                     Log.e(TAG, "Error creating page local color bitmap", e);
                     continue;
                 }
-                WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
+                WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount);
                 target.recycle();
                 WallpaperColors currentColor = page.getColors(area);
 
@@ -1837,12 +1888,14 @@
                                 + " local color callback for area" + area + " for page " + pageIndx
                                 + " of " + numPages);
                     }
-                    try {
-                        mConnection.onLocalWallpaperColorsChanged(area, color,
-                                mDisplayContext.getDisplayId());
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
-                    }
+                    mHandler.post(() -> {
+                        try {
+                            mConnection.onLocalWallpaperColorsChanged(area, color,
+                                    mDisplayContext.getDisplayId());
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+                        }
+                    });
                 }
             }
             Trace.endSection();
@@ -1868,16 +1921,17 @@
             return new RectF(left, in.top, right, in.bottom);
         }
 
+        @GuardedBy("mLock")
         private void resetWindowPages() {
             if (supportsLocalColorExtraction()) return;
             if (!mResetWindowPages) return;
             mResetWindowPages = false;
-            mLastWindowPage = -1;
             for (int i = 0; i < mWindowPages.length; i++) {
                 mWindowPages[i].setLastUpdateTime(0L);
             }
         }
 
+        @GuardedBy("mLock")
         private int getRectFPage(RectF area, float step) {
             if (!isValid(area)) return 0;
             if (!validStep(step)) return 0;
@@ -1898,12 +1952,12 @@
             if (DEBUG) {
                 Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
             }
-            mHandler.post(() -> {
-                mLocalColorsToAdd.addAll(regions);
-                processLocalColors(mPendingXOffset, mPendingYOffset);
+            mBackgroundHandler.post(() -> {
+                synchronized (mLock) {
+                    mLocalColorsToAdd.addAll(regions);
+                }
+                processLocalColors();
             });
-
-
         }
 
         /**
@@ -1913,16 +1967,18 @@
          */
         public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
             if (supportsLocalColorExtraction()) return;
-            mHandler.post(() -> {
-                float step = mPendingXOffsetStep;
-                mLocalColorsToAdd.removeAll(regions);
-                mLocalColorAreas.removeAll(regions);
-                if (!validStep(step)) {
-                    return;
-                }
-                for (int i = 0; i < mWindowPages.length; i++) {
-                    for (int j = 0; j < regions.size(); j++) {
-                        mWindowPages[i].removeArea(regions.get(j));
+            mBackgroundHandler.post(() -> {
+                synchronized (mLock) {
+                    float step = mPendingXOffsetStep;
+                    mLocalColorsToAdd.removeAll(regions);
+                    mLocalColorAreas.removeAll(regions);
+                    if (!validStep(step)) {
+                        return;
+                    }
+                    for (int i = 0; i < mWindowPages.length; i++) {
+                        for (int j = 0; j < regions.size(); j++) {
+                            mWindowPages[i].removeArea(regions.get(j));
+                        }
                     }
                 }
             });
@@ -1940,7 +1996,7 @@
         }
 
         private boolean validStep(float step) {
-            return !PROHIBITED_STEPS.contains(step) && step > 0. && step <= 1.;
+            return !Float.isNaN(step) && step > 0f && step <= 1f;
         }
 
         void doCommand(WallpaperCommand cmd) {
@@ -2579,6 +2635,9 @@
     @Override
     public void onCreate() {
         Trace.beginSection("WPMS.onCreate");
+        mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor");
+        mBackgroundThread.start();
+        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
         super.onCreate();
         Trace.endSection();
     }
@@ -2591,6 +2650,7 @@
             engineWrapper.destroy();
         }
         mActiveEngines.clear();
+        mBackgroundThread.quitSafely();
         Trace.endSection();
     }
 
diff --git a/core/java/android/util/DataUnit.java b/core/java/android/util/DataUnit.java
index cf045b8..cc33af3 100644
--- a/core/java/android/util/DataUnit.java
+++ b/core/java/android/util/DataUnit.java
@@ -36,9 +36,11 @@
     KILOBYTES { @Override public long toBytes(long v) { return v * 1_000; } },
     MEGABYTES { @Override public long toBytes(long v) { return v * 1_000_000; } },
     GIGABYTES { @Override public long toBytes(long v) { return v * 1_000_000_000; } },
+    TERABYTES { @Override public long toBytes(long v) { return v * 1_000_000_000_000L; } },
     KIBIBYTES { @Override public long toBytes(long v) { return v * 1_024; } },
     MEBIBYTES { @Override public long toBytes(long v) { return v * 1_048_576; } },
-    GIBIBYTES { @Override public long toBytes(long v) { return v * 1_073_741_824; } };
+    GIBIBYTES { @Override public long toBytes(long v) { return v * 1_073_741_824; } },
+    TEBIBYTES { @Override public long toBytes(long v) { return v * 1_099_511_627_776L; } };
 
     public long toBytes(long v) {
         throw new AbstractMethodError();
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index cc83dec..cd03d83 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -71,7 +71,7 @@
      * Creates a new SparseArray containing no mappings.
      */
     public SparseArray() {
-        this(10);
+        this(0);
     }
 
     /**
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index c145b20..12a9900 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -51,7 +51,7 @@
      * Creates a new SparseBooleanArray containing no mappings.
      */
     public SparseBooleanArray() {
-        this(10);
+        this(0);
     }
 
     /**
diff --git a/core/java/android/util/SparseDoubleArray.java b/core/java/android/util/SparseDoubleArray.java
index ee2e3ce..4b0cbe4 100644
--- a/core/java/android/util/SparseDoubleArray.java
+++ b/core/java/android/util/SparseDoubleArray.java
@@ -50,7 +50,7 @@
 
     /** Creates a new SparseDoubleArray containing no mappings. */
     public SparseDoubleArray() {
-        this(10);
+        this(0);
     }
 
     /**
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index d4f6685..0e98c28 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -58,7 +58,7 @@
      * Creates a new SparseIntArray containing no mappings.
      */
     public SparseIntArray() {
-        this(10);
+        this(0);
     }
 
     /**
diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java
index b739e37..e86b647 100644
--- a/core/java/android/util/SparseLongArray.java
+++ b/core/java/android/util/SparseLongArray.java
@@ -51,7 +51,7 @@
      * Creates a new SparseLongArray containing no mappings.
      */
     public SparseLongArray() {
-        this(10);
+        this(0);
     }
 
     /**
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 9225cd9..61864d7 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -1183,8 +1183,10 @@
      */
     @NonNull
     public LightsManager getLightsManager() {
-        if (mLightsManager == null) {
-            mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId);
+        synchronized (mMotionRanges) {
+            if (mLightsManager == null) {
+                mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId);
+            }
         }
         return mLightsManager;
     }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b46a68c..d8b6b7b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -851,10 +851,14 @@
             }
             mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
 
-            if (mViewVisibility) {
-                surfaceUpdateTransaction.show(mSurfaceControl);
-            } else {
-                surfaceUpdateTransaction.hide(mSurfaceControl);
+            // Only control visibility if we're not hardware-accelerated. Otherwise we'll
+            // let renderthread drive since offscreen SurfaceControls should not be visible.
+            if (!isHardwareAccelerated()) {
+                if (mViewVisibility) {
+                    surfaceUpdateTransaction.show(mSurfaceControl);
+                } else {
+                    surfaceUpdateTransaction.hide(mSurfaceControl);
+                }
             }
 
             updateBackgroundVisibility(surfaceUpdateTransaction);
@@ -1417,12 +1421,10 @@
     }
 
     private final Rect mRTLastReportedPosition = new Rect();
-    private final Point mRTLastReportedSurfaceSize = new Point();
 
     private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
         private final int mRtSurfaceWidth;
         private final int mRtSurfaceHeight;
-        private boolean mRtFirst = true;
         private final SurfaceControl.Transaction mPositionChangedTransaction =
                 new SurfaceControl.Transaction();
 
@@ -1433,15 +1435,6 @@
 
         @Override
         public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
-            if (!mRtFirst && (mRTLastReportedPosition.left == left
-                    && mRTLastReportedPosition.top == top
-                    && mRTLastReportedPosition.right == right
-                    && mRTLastReportedPosition.bottom == bottom
-                    && mRTLastReportedSurfaceSize.x == mRtSurfaceWidth
-                    && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight)) {
-                return;
-            }
-            mRtFirst = false;
             try {
                 if (DEBUG_POSITION) {
                     Log.d(TAG, String.format(
@@ -1452,8 +1445,8 @@
                 }
                 synchronized (mSurfaceControlLock) {
                     if (mSurfaceControl == null) return;
+
                     mRTLastReportedPosition.set(left, top, right, bottom);
-                    mRTLastReportedSurfaceSize.set(mRtSurfaceWidth, mRtSurfaceHeight);
                     onSetSurfacePositionAndScale(mPositionChangedTransaction, mSurfaceControl,
                             mRTLastReportedPosition.left /*positionLeft*/,
                             mRTLastReportedPosition.top /*positionTop*/,
@@ -1461,10 +1454,8 @@
                                     / (float) mRtSurfaceWidth /*postScaleX*/,
                             mRTLastReportedPosition.height()
                                     / (float) mRtSurfaceHeight /*postScaleY*/);
-                    if (mViewVisibility) {
-                        // b/131239825
-                        mPositionChangedTransaction.show(mSurfaceControl);
-                    }
+
+                    mPositionChangedTransaction.show(mSurfaceControl);
                 }
                 applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
             } catch (Exception ex) {
@@ -1490,7 +1481,6 @@
                         System.identityHashCode(this), frameNumber));
             }
             mRTLastReportedPosition.setEmpty();
-            mRTLastReportedSurfaceSize.set(-1, -1);
 
             // positionLost can be called while UI thread is un-paused.
             synchronized (mSurfaceControlLock) {
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 02dafd4..7f0a651 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -13,3 +13,5 @@
 per-file SpellChecker.java = file:../view/inputmethod/OWNERS
 
 per-file RemoteViews* = file:../appwidget/OWNERS
+
+per-file Toast.java = juliacr@google.com, jeffdq@google.com
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 0d02683..a1d571f 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -1,4 +1,6 @@
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file UnlaunchableAppActivity.java = file:/core/java/android/app/admin/WorkProfile_OWNERS
+per-file IntentForwarderActivity.java = file:/core/java/android/app/admin/WorkProfile_OWNERS
 per-file *Resolver* = file:/packages/SystemUI/OWNERS
 per-file *Chooser* = file:/packages/SystemUI/OWNERS
 per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 9fae211..2b08a55 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -74,6 +74,9 @@
         public static final Flag OTP_REDACTION =
                 devFlag("persist.sysui.notification.otp_redaction");
 
+        /** Gating the removal of sorting-notifications-by-interruptiveness. */
+        public static final Flag NO_SORT_BY_INTERRUPTIVENESS =
+                devFlag("persist.sysui.notification.no_sort_by_interruptiveness");
     }
 
     //// == End of flags.  Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/expresslog/Counter.java b/core/java/com/android/internal/expresslog/Counter.java
index afdbdc8..4a46d91 100644
--- a/core/java/com/android/internal/expresslog/Counter.java
+++ b/core/java/com/android/internal/expresslog/Counter.java
@@ -36,6 +36,16 @@
     }
 
     /**
+     * Increments Telemetry Express Counter metric by 1
+     * @param metricId to log, no-op if metricId is not defined in the TeX catalog
+     * @param uid used as a dimension for the count metric
+     * @hide
+     */
+    public static void logIncrementWithUid(@NonNull String metricId, int uid) {
+        logIncrementWithUid(metricId, uid, 1);
+    }
+
+    /**
      * Increments Telemetry Express Counter metric by arbitrary value
      * @param metricId to log, no-op if metricId is not defined in the TeX catalog
      * @param amount to increment counter
@@ -45,4 +55,17 @@
         final long metricIdHash = Utils.hashString(metricId);
         FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount);
     }
+
+    /**
+     * Increments Telemetry Express Counter metric by arbitrary value
+     * @param metricId to log, no-op if metricId is not defined in the TeX catalog
+     * @param uid used as a dimension for the count metric
+     * @param amount to increment counter
+     * @hide
+     */
+    public static void logIncrementWithUid(@NonNull String metricId, int uid, long amount) {
+        final long metricIdHash = Utils.hashString(metricId);
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.EXPRESS_UID_EVENT_REPORTED, metricIdHash, amount, uid);
+    }
 }
diff --git a/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS b/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
new file mode 100644
index 0000000..7755000
--- /dev/null
+++ b/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
@@ -0,0 +1,3 @@
+# TODO(b/274465475): Migrate LatencyTracker testing to its own module
+marcinoc@google.com
+ilkos@google.com
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 43a9f5f..3c06755 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -48,6 +48,7 @@
 import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;
 
 import android.Manifest;
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -55,7 +56,6 @@
 import android.app.ActivityThread;
 import android.content.Context;
 import android.os.Build;
-import android.os.ConditionVariable;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.provider.DeviceConfig;
@@ -79,7 +79,7 @@
  * Class to track various latencies in SystemUI. It then writes the latency to statsd and also
  * outputs it to logcat so these latencies can be captured by tests and then used for dashboards.
  * <p>
- * This is currently only in Keyguard so it can be shared between SystemUI and Keyguard, but
+ * This is currently only in Keyguard. It can be shared between SystemUI and Keyguard, but
  * eventually we'd want to merge these two packages together so Keyguard can use common classes
  * that are shared with SystemUI.
  */
@@ -285,8 +285,6 @@
             UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN,
     };
 
-    private static LatencyTracker sLatencyTracker;
-
     private final Object mLock = new Object();
     @GuardedBy("mLock")
     private final SparseArray<Session> mSessions = new SparseArray<>();
@@ -294,20 +292,21 @@
     private final SparseArray<ActionProperties> mActionPropertiesMap = new SparseArray<>();
     @GuardedBy("mLock")
     private boolean mEnabled;
-    @VisibleForTesting
-    public final ConditionVariable mDeviceConfigPropertiesUpdated = new ConditionVariable();
 
-    public static LatencyTracker getInstance(Context context) {
-        if (sLatencyTracker == null) {
-            synchronized (LatencyTracker.class) {
-                if (sLatencyTracker == null) {
-                    sLatencyTracker = new LatencyTracker();
-                }
-            }
-        }
-        return sLatencyTracker;
+    // Wrapping this in a holder class achieves lazy loading behavior
+    private static final class SLatencyTrackerHolder {
+        private static final LatencyTracker sLatencyTracker = new LatencyTracker();
     }
 
+    public static LatencyTracker getInstance(Context context) {
+        return SLatencyTrackerHolder.sLatencyTracker;
+    }
+
+    /**
+     * Constructor for LatencyTracker
+     *
+     * <p>This constructor is only visible for test classes to inject their own consumer callbacks
+     */
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
     @VisibleForTesting
     public LatencyTracker() {
@@ -349,11 +348,8 @@
                         properties.getInt(actionName + TRACE_THRESHOLD_SUFFIX,
                                 legacyActionTraceThreshold)));
             }
-            if (DEBUG) {
-                Log.d(TAG, "updated action properties: " + mActionPropertiesMap);
-            }
+            onDeviceConfigPropertiesUpdated(mActionPropertiesMap);
         }
-        mDeviceConfigPropertiesUpdated.open();
     }
 
     /**
@@ -477,7 +473,7 @@
      */
     public void onActionStart(@Action int action, String tag) {
         synchronized (mLock) {
-            if (!isEnabled()) {
+            if (!isEnabled(action)) {
                 return;
             }
             // skip if the action is already instrumenting.
@@ -501,7 +497,7 @@
      */
     public void onActionEnd(@Action int action) {
         synchronized (mLock) {
-            if (!isEnabled()) {
+            if (!isEnabled(action)) {
                 return;
             }
             Session session = mSessions.get(action);
@@ -540,6 +536,24 @@
     }
 
     /**
+     * Testing API to get the time when a given action was started.
+     *
+     * @param action Action which to retrieve start time from
+     * @return Elapsed realtime timestamp when the action started. -1 if the action is not active.
+     * @hide
+     */
+    @VisibleForTesting
+    @ElapsedRealtimeLong
+    public long getActiveActionStartTime(@Action int action) {
+        synchronized (mLock) {
+            if (mSessions.contains(action)) {
+                return mSessions.get(action).mStartRtc;
+            }
+            return -1;
+        }
+    }
+
+    /**
      * Logs an action that has started and ended. This needs to be called from the main thread.
      *
      * @param action   The action to end. One of the ACTION_* values.
@@ -549,6 +563,9 @@
         boolean shouldSample;
         int traceThreshold;
         synchronized (mLock) {
+            if (!isEnabled(action)) {
+                return;
+            }
             ActionProperties actionProperties = mActionPropertiesMap.get(action);
             if (actionProperties == null) {
                 return;
@@ -559,28 +576,24 @@
             traceThreshold = actionProperties.getTraceThreshold();
         }
 
-        if (traceThreshold > 0 && duration >= traceThreshold) {
-            PerfettoTrigger.trigger(getTraceTriggerNameForAction(action));
+        boolean shouldTriggerPerfettoTrace = traceThreshold > 0 && duration >= traceThreshold;
+
+        if (DEBUG) {
+            Log.i(TAG, "logAction: " + getNameOfAction(STATSD_ACTION[action])
+                    + " duration=" + duration
+                    + " shouldSample=" + shouldSample
+                    + " shouldTriggerPerfettoTrace=" + shouldTriggerPerfettoTrace);
         }
 
-        logActionDeprecated(action, duration, shouldSample);
-    }
-
-    /**
-     * Logs an action that has started and ended. This needs to be called from the main thread.
-     *
-     * @param action The action to end. One of the ACTION_* values.
-     * @param duration The duration of the action in ms.
-     * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
-     */
-    public static void logActionDeprecated(
-            @Action int action, int duration, boolean writeToStatsLog) {
-        Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
         EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
-
-        if (writeToStatsLog) {
-            FrameworkStatsLog.write(
-                    FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
+        if (shouldTriggerPerfettoTrace) {
+            onTriggerPerfetto(getTraceTriggerNameForAction(action));
+        }
+        if (shouldSample) {
+            onLogToFrameworkStats(
+                    new FrameworkStatsLogEvent(action, FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED,
+                            STATSD_ACTION[action], duration)
+            );
         }
     }
 
@@ -642,10 +655,10 @@
     }
 
     @VisibleForTesting
-    static class ActionProperties {
+    public static class ActionProperties {
         static final String ENABLE_SUFFIX = "_enable";
         static final String SAMPLE_INTERVAL_SUFFIX = "_sample_interval";
-        // TODO: migrate all usages of the legacy trace theshold property
+        // TODO: migrate all usages of the legacy trace threshold property
         static final String LEGACY_TRACE_THRESHOLD_SUFFIX = "";
         static final String TRACE_THRESHOLD_SUFFIX = "_trace_threshold";
 
@@ -655,7 +668,8 @@
         private final int mSamplingInterval;
         private final int mTraceThreshold;
 
-        ActionProperties(
+        @VisibleForTesting
+        public ActionProperties(
                 @Action int action,
                 boolean enabled,
                 int samplingInterval,
@@ -668,20 +682,24 @@
             this.mTraceThreshold = traceThreshold;
         }
 
+        @VisibleForTesting
         @Action
-        int getAction() {
+        public int getAction() {
             return mAction;
         }
 
-        boolean isEnabled() {
+        @VisibleForTesting
+        public boolean isEnabled() {
             return mEnabled;
         }
 
-        int getSamplingInterval() {
+        @VisibleForTesting
+        public int getSamplingInterval() {
             return mSamplingInterval;
         }
 
-        int getTraceThreshold() {
+        @VisibleForTesting
+        public int getTraceThreshold() {
             return mTraceThreshold;
         }
 
@@ -694,5 +712,103 @@
                     + ", mTraceThreshold=" + mTraceThreshold
                     + "}";
         }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null) {
+                return false;
+            }
+            if (!(o instanceof ActionProperties)) {
+                return false;
+            }
+            ActionProperties that = (ActionProperties) o;
+            return mAction == that.mAction
+                    && mEnabled == that.mEnabled
+                    && mSamplingInterval == that.mSamplingInterval
+                    && mTraceThreshold == that.mTraceThreshold;
+        }
+
+        @Override
+        public int hashCode() {
+            int _hash = 1;
+            _hash = 31 * _hash + mAction;
+            _hash = 31 * _hash + Boolean.hashCode(mEnabled);
+            _hash = 31 * _hash + mSamplingInterval;
+            _hash = 31 * _hash + mTraceThreshold;
+            return _hash;
+        }
+    }
+
+    /**
+     * Testing method intended to be overridden to determine when the LatencyTracker's device
+     * properties are updated.
+     */
+    @VisibleForTesting
+    public void onDeviceConfigPropertiesUpdated(SparseArray<ActionProperties> actionProperties) {
+        if (DEBUG) {
+            Log.d(TAG, "onDeviceConfigPropertiesUpdated: " + actionProperties);
+        }
+    }
+
+    /**
+     * Testing class intended to be overridden to determine when LatencyTracker triggers perfetto.
+     */
+    @VisibleForTesting
+    public void onTriggerPerfetto(String triggerName) {
+        if (DEBUG) {
+            Log.i(TAG, "onTriggerPerfetto: triggerName=" + triggerName);
+        }
+        PerfettoTrigger.trigger(triggerName);
+    }
+
+    /**
+     * Testing method intended to be overridden to determine when LatencyTracker writes to
+     * FrameworkStatsLog.
+     */
+    @VisibleForTesting
+    public void onLogToFrameworkStats(FrameworkStatsLogEvent event) {
+        if (DEBUG) {
+            Log.i(TAG, "onLogToFrameworkStats: event=" + event);
+        }
+        FrameworkStatsLog.write(event.logCode, event.statsdAction, event.durationMillis);
+    }
+
+    /**
+     * Testing class intended to reject what should be written to the {@link FrameworkStatsLog}
+     *
+     * <p>This class is used in {@link #onLogToFrameworkStats(FrameworkStatsLogEvent)} for test code
+     * to observer when and what information is being logged by {@link LatencyTracker}
+     */
+    @VisibleForTesting
+    public static class FrameworkStatsLogEvent {
+
+        @VisibleForTesting
+        public final int action;
+        @VisibleForTesting
+        public final int logCode;
+        @VisibleForTesting
+        public final int statsdAction;
+        @VisibleForTesting
+        public final int durationMillis;
+
+        private FrameworkStatsLogEvent(int action, int logCode, int statsdAction,
+                int durationMillis) {
+            this.action = action;
+            this.logCode = logCode;
+            this.statsdAction = statsdAction;
+            this.durationMillis = durationMillis;
+        }
+
+        @Override
+        public String toString() {
+            return "FrameworkStatsLogEvent{"
+                    + " logCode=" + logCode
+                    + ", statsdAction=" + statsdAction
+                    + ", durationMillis=" + durationMillis
+                    + "}";
+        }
     }
 }
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 1808bd5..9be8ea7 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -6,3 +6,4 @@
 per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
 per-file *Dump* = file:/core/java/com/android/internal/util/dump/OWNERS
 per-file *Screenshot* = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *LatencyTracker* = file:/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 092f6e5..0f1e558 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4482,6 +4482,12 @@
     <permission android:name="android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- Allows specifying candidate credential providers to be queried in Credential Manager
+    get flows, or to be preferred as a default in the Credential Manager create flows.
+     <p>Protection level: normal -->
+    <permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS"
+                android:protectionLevel="normal" />
+
     <!-- Allows a browser to invoke credential manager APIs on behalf of another RP.
         <p>Protection level: normal -->
     <permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 41c8b4a..b53502d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -10089,4 +10089,27 @@
         <!-- Maximum width of height drawable. Drawables exceeding this size will be downsampled. -->
         <attr name="maxDrawableHeight" format="dimension"/>
     </declare-styleable>
+
+    <!-- =============================== -->
+    <!-- Credential Manager attributes -->
+    <!-- =============================== -->
+    <eat-comment />
+
+    <!-- Contains Credential Provider related metadata. Since providers are exposed
+         as services these should live under the service.
+    -->
+    <declare-styleable name="CredentialProvider">
+        <!-- A string that is displayed to the user in the Credential Manager settings
+             screen that can be used to provide more information about a provider. For
+             longer strings (40 char) it will be truncated. If multiple services
+             show the subtitle then the string will be joined together. -->
+        <attr name="settingsSubtitle" format="string" />
+    </declare-styleable>
+
+    <!-- A list of capabilities that indicates to the OS what kinds of credentials
+             this provider supports. This list is defined in CredentialProviderService. -->
+    <declare-styleable name="CredentialProvider_Capabilities" parent="CredentialProvider">
+        <!-- An individual capability declared by the provider. -->
+        <attr name="capability" format="string" />
+    </declare-styleable>
     </resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 696c0ed..12a6c52 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -130,6 +130,8 @@
     <public name="focusedSearchResultHighlightColor" />
     <public name="stylusHandwritingSettingsActivity" />
     <public name="windowNoMoveAnimation" />
+    <public name="settingsSubtitle" />
+    <public name="capability" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01cd0000">
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index e811bb6..3ea1592 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -20,6 +20,7 @@
         "BinderProxyCountingTestService/src/**/*.java",
         "BinderDeathRecipientHelperApp/src/**/*.java",
         "aidl/**/I*.aidl",
+        ":FrameworksCoreTestDoubles-sources",
     ],
 
     aidl: {
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 34d669b..6debbfe 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -63,7 +63,6 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
-import android.app.Notification.CallStyle;
 import android.content.Context;
 import android.content.Intent;
 import android.content.LocusId;
@@ -1039,6 +1038,69 @@
     }
 
     @Test
+    public void areIconsDifferent_sameSmallIcon_false() {
+        Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build();
+        Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(1).build();
+
+        assertThat(Notification.areIconsDifferent(n1, n2)).isFalse();
+    }
+
+    @Test
+    public void areIconsDifferent_differentSmallIcon_true() {
+        Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build();
+        Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(2).build();
+
+        assertThat(Notification.areIconsDifferent(n1, n2)).isTrue();
+    }
+
+    @Test
+    public void areIconsDifferent_sameLargeIcon_false() {
+        Icon icon1 = Icon.createWithContentUri("uri");
+        Icon icon2 = Icon.createWithContentUri("uri");
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .setSmallIcon(1).setLargeIcon(icon1).build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .setSmallIcon(1).setLargeIcon(icon2).build();
+
+        // Note that this will almost certainly not happen for Icons created from Bitmaps, since
+        // their serialization/deserialization of Bitmaps (app -> system_process) results in a
+        // different getGenerationId() value. :(
+        assertThat(Notification.areIconsDifferent(n1, n2)).isFalse();
+    }
+
+    @Test
+    public void areIconsDifferent_differentLargeIcon_true() {
+        Icon icon1 = Icon.createWithContentUri("uri1");
+        Icon icon2 = Icon.createWithContentUri("uri2");
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .setSmallIcon(1).setLargeIcon(icon1).build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .setSmallIcon(2).setLargeIcon(icon2).build();
+
+        assertThat(Notification.areIconsDifferent(n1, n2)).isTrue();
+    }
+
+    @Test
+    public void areIconsDifferent_addedLargeIcon_true() {
+        Icon icon = Icon.createWithContentUri("uri");
+        Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build();
+        Notification n2 = new Notification.Builder(mContext, "test")
+                .setSmallIcon(2).setLargeIcon(icon).build();
+
+        assertThat(Notification.areIconsDifferent(n1, n2)).isTrue();
+    }
+
+    @Test
+    public void areIconsDifferent_removedLargeIcon_true() {
+        Icon icon = Icon.createWithContentUri("uri");
+        Notification n1 = new Notification.Builder(mContext, "test")
+                .setSmallIcon(1).setLargeIcon(icon).build();
+        Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(2).build();
+
+        assertThat(Notification.areIconsDifferent(n1, n2)).isTrue();
+    }
+
+    @Test
     public void testStyleChangeVisiblyDifferent_noStyles() {
         Notification.Builder n1 = new Notification.Builder(mContext, "test");
         Notification.Builder n2 = new Notification.Builder(mContext, "test");
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 91fbe00..394ff0a 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -54,6 +54,7 @@
 import android.content.Context;
 import android.os.FileUtils.MemoryPipe;
 import android.provider.DocumentsContract.Document;
+import android.util.DataUnit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -504,31 +505,45 @@
 
     @Test
     public void testRoundStorageSize() throws Exception {
-        final long M128 = 128000000L;
-        final long M256 = 256000000L;
-        final long M512 = 512000000L;
-        final long G1 = 1000000000L;
-        final long G2 = 2000000000L;
-        final long G16 = 16000000000L;
-        final long G32 = 32000000000L;
-        final long G64 = 64000000000L;
+        final long GB1 = DataUnit.GIGABYTES.toBytes(1);
+        final long GiB1 = DataUnit.GIBIBYTES.toBytes(1);
+        final long GB2 = DataUnit.GIGABYTES.toBytes(2);
+        final long GiB2 = DataUnit.GIBIBYTES.toBytes(2);
+        final long GiB128 = DataUnit.GIBIBYTES.toBytes(128);
+        final long GB256 = DataUnit.GIGABYTES.toBytes(256);
+        final long GiB256 = DataUnit.GIBIBYTES.toBytes(256);
+        final long GB512 = DataUnit.GIGABYTES.toBytes(512);
+        final long GiB512 = DataUnit.GIBIBYTES.toBytes(512);
+        final long TB1 = DataUnit.TERABYTES.toBytes(1);
+        final long TiB1 = DataUnit.TEBIBYTES.toBytes(1);
+        final long TB2 = DataUnit.TERABYTES.toBytes(2);
+        final long TiB2 = DataUnit.TEBIBYTES.toBytes(2);
+        final long TB4 = DataUnit.TERABYTES.toBytes(4);
+        final long TiB4 = DataUnit.TEBIBYTES.toBytes(4);
+        final long TB8 = DataUnit.TERABYTES.toBytes(8);
+        final long TiB8 = DataUnit.TEBIBYTES.toBytes(8);
 
-        assertEquals(M128, roundStorageSize(M128));
-        assertEquals(M256, roundStorageSize(M128 + 1));
-        assertEquals(M256, roundStorageSize(M256 - 1));
-        assertEquals(M256, roundStorageSize(M256));
-        assertEquals(M512, roundStorageSize(M256 + 1));
-        assertEquals(M512, roundStorageSize(M512 - 1));
-        assertEquals(M512, roundStorageSize(M512));
-        assertEquals(G1, roundStorageSize(M512 + 1));
-        assertEquals(G1, roundStorageSize(G1));
-        assertEquals(G2, roundStorageSize(G1 + 1));
+        assertEquals(GB1, roundStorageSize(GB1 - 1));
+        assertEquals(GB1, roundStorageSize(GB1));
+        assertEquals(GB1, roundStorageSize(GB1 + 1));
+        assertEquals(GB1, roundStorageSize(GiB1 - 1));
+        assertEquals(GB1, roundStorageSize(GiB1));
+        assertEquals(GB2, roundStorageSize(GiB1 + 1));
+        assertEquals(GB2, roundStorageSize(GiB2));
 
-        assertEquals(G16, roundStorageSize(G16));
-        assertEquals(G32, roundStorageSize(G16 + 1));
-        assertEquals(G32, roundStorageSize(G32 - 1));
-        assertEquals(G32, roundStorageSize(G32));
-        assertEquals(G64, roundStorageSize(G32 + 1));
+        assertEquals(GB256, roundStorageSize(GiB128 + 1));
+        assertEquals(GB256, roundStorageSize(GiB256));
+        assertEquals(GB512, roundStorageSize(GiB256 + 1));
+        assertEquals(GB512, roundStorageSize(GiB512));
+        assertEquals(TB1, roundStorageSize(GiB512 + 1));
+        assertEquals(TB1, roundStorageSize(TiB1));
+        assertEquals(TB2, roundStorageSize(TiB1 + 1));
+        assertEquals(TB2, roundStorageSize(TiB2));
+        assertEquals(TB4, roundStorageSize(TiB2 + 1));
+        assertEquals(TB4, roundStorageSize(TiB4));
+        assertEquals(TB8, roundStorageSize(TiB4 + 1));
+        assertEquals(TB8, roundStorageSize(TiB8));
+        assertEquals(TB1, roundStorageSize(1013077688320L)); // b/268571529
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/util/DataUnitTest.java b/core/tests/coretests/src/android/util/DataUnitTest.java
index ec296b7..034cbdd 100644
--- a/core/tests/coretests/src/android/util/DataUnitTest.java
+++ b/core/tests/coretests/src/android/util/DataUnitTest.java
@@ -26,11 +26,13 @@
         assertEquals(12_000L, DataUnit.KILOBYTES.toBytes(12));
         assertEquals(12_000_000L, DataUnit.MEGABYTES.toBytes(12));
         assertEquals(12_000_000_000L, DataUnit.GIGABYTES.toBytes(12));
+        assertEquals(12_000_000_000_000L, DataUnit.TERABYTES.toBytes(12));
     }
 
     public void testIec() throws Exception {
         assertEquals(12_288L, DataUnit.KIBIBYTES.toBytes(12));
         assertEquals(12_582_912L, DataUnit.MEBIBYTES.toBytes(12));
         assertEquals(12_884_901_888L, DataUnit.GIBIBYTES.toBytes(12));
+        assertEquals(13_194_139_533_312L, DataUnit.TEBIBYTES.toBytes(12));
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
new file mode 100644
index 0000000..e6f10ad
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION;
+import static com.android.internal.util.FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED;
+import static com.android.internal.util.LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * This test class verifies the additional methods which {@link FakeLatencyTracker} exposes.
+ *
+ * <p>The typical {@link LatencyTracker} behavior test coverage is present in
+ * {@link LatencyTrackerTest}
+ */
+@RunWith(AndroidJUnit4.class)
+public class FakeLatencyTrackerTest {
+
+    private FakeLatencyTracker mFakeLatencyTracker;
+
+    @Before
+    public void setUp() throws Exception {
+        mFakeLatencyTracker = FakeLatencyTracker.create();
+    }
+
+    @Test
+    public void testForceEnabled() throws Exception {
+        mFakeLatencyTracker.logAction(ACTION_SHOW_VOICE_INTERACTION, 1234);
+
+        assertThat(mFakeLatencyTracker.getEventsWrittenToFrameworkStats(
+                ACTION_SHOW_VOICE_INTERACTION)).isEmpty();
+
+        mFakeLatencyTracker.forceEnabled(ACTION_SHOW_VOICE_INTERACTION, 1000);
+        mFakeLatencyTracker.logAction(ACTION_SHOW_VOICE_INTERACTION, 1234);
+        List<LatencyTracker.FrameworkStatsLogEvent> events =
+                mFakeLatencyTracker.getEventsWrittenToFrameworkStats(
+                        ACTION_SHOW_VOICE_INTERACTION);
+        assertThat(events).hasSize(1);
+        assertThat(events.get(0).logCode).isEqualTo(UI_ACTION_LATENCY_REPORTED);
+        assertThat(events.get(0).statsdAction).isEqualTo(
+                UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION);
+        assertThat(events.get(0).durationMillis).isEqualTo(1234);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
index d1f0b5e..645324d 100644
--- a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
@@ -16,19 +16,22 @@
 
 package com.android.internal.util;
 
+import static android.provider.DeviceConfig.NAMESPACE_LATENCY_TRACKER;
 import static android.text.TextUtils.formatSimple;
 
+import static com.android.internal.util.FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED;
 import static com.android.internal.util.LatencyTracker.STATSD_ACTION;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.provider.DeviceConfig;
-import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.util.LatencyTracker.ActionProperties;
+
 import com.google.common.truth.Expect;
 
 import org.junit.Before;
@@ -38,7 +41,6 @@
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.time.Duration;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -49,27 +51,23 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class LatencyTrackerTest {
-    private static final String TAG = LatencyTrackerTest.class.getSimpleName();
     private static final String ENUM_NAME_PREFIX = "UIACTION_LATENCY_REPORTED__ACTION__";
-    private static final String ACTION_ENABLE_SUFFIX = "_enable";
-    private static final Duration TEST_TIMEOUT = Duration.ofMillis(500);
 
     @Rule
     public final Expect mExpect = Expect.create();
 
+    // Fake is used because it tests the real logic of LatencyTracker, and it only fakes the
+    // outcomes (PerfettoTrigger and FrameworkStatsLog).
+    private FakeLatencyTracker mLatencyTracker;
+
     @Before
-    public void setUp() {
-        DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
-                LatencyTracker.SETTINGS_ENABLED_KEY);
-        getAllActions().forEach(action -> {
-            DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
-                    action.getName().toLowerCase() + ACTION_ENABLE_SUFFIX);
-        });
+    public void setUp() throws Exception {
+        mLatencyTracker = FakeLatencyTracker.create();
     }
 
     @Test
     public void testCujsMapToEnumsCorrectly() {
-        List<Field> actions = getAllActions();
+        List<Field> actions = getAllActionFields();
         Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
                 .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
                         && Modifier.isStatic(f.getModifiers())
@@ -101,7 +99,7 @@
 
     @Test
     public void testCujTypeEnumCorrectlyDefined() throws Exception {
-        List<Field> cujEnumFields = getAllActions();
+        List<Field> cujEnumFields = getAllActionFields();
         HashSet<Integer> allValues = new HashSet<>();
         for (Field field : cujEnumFields) {
             int fieldValue = field.getInt(null);
@@ -118,92 +116,242 @@
     }
 
     @Test
-    public void testIsEnabled_globalEnabled() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+    public void testIsEnabled_trueWhenGlobalEnabled() throws Exception {
+        DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER,
                 LatencyTracker.SETTINGS_ENABLED_KEY, "true", false);
-        LatencyTracker latencyTracker = new LatencyTracker();
-        waitForLatencyTrackerToUpdateProperties(latencyTracker);
-        assertThat(latencyTracker.isEnabled()).isTrue();
+        mLatencyTracker.waitForGlobalEnabledState(true);
+        mLatencyTracker.waitForAllPropertiesEnableState(true);
+
+        //noinspection deprecation
+        assertThat(mLatencyTracker.isEnabled()).isTrue();
     }
 
     @Test
-    public void testIsEnabled_globalDisabled() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+    public void testIsEnabled_falseWhenGlobalDisabled() throws Exception {
+        DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER,
                 LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
-        LatencyTracker latencyTracker = new LatencyTracker();
-        waitForLatencyTrackerToUpdateProperties(latencyTracker);
-        assertThat(latencyTracker.isEnabled()).isFalse();
+        mLatencyTracker.waitForGlobalEnabledState(false);
+        mLatencyTracker.waitForAllPropertiesEnableState(false);
+
+        //noinspection deprecation
+        assertThat(mLatencyTracker.isEnabled()).isFalse();
     }
 
     @Test
-    public void testIsEnabledAction_useGlobalValueWhenActionEnableIsNotSet() {
-        LatencyTracker latencyTracker = new LatencyTracker();
+    public void testIsEnabledAction_useGlobalValueWhenActionEnableIsNotSet()
+            throws Exception {
         // using a single test action, but this applies to all actions
         int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
-        Log.i(TAG, "setting property=" + LatencyTracker.SETTINGS_ENABLED_KEY + ", value=true");
-        latencyTracker.mDeviceConfigPropertiesUpdated.close();
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+        DeviceConfig.deleteProperty(NAMESPACE_LATENCY_TRACKER,
+                "action_show_voice_interaction_enable");
+        mLatencyTracker.waitForAllPropertiesEnableState(false);
+        DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER,
                 LatencyTracker.SETTINGS_ENABLED_KEY, "true", false);
-        waitForLatencyTrackerToUpdateProperties(latencyTracker);
-        assertThat(
-                latencyTracker.isEnabled(action)).isTrue();
+        mLatencyTracker.waitForGlobalEnabledState(true);
+        mLatencyTracker.waitForAllPropertiesEnableState(true);
 
-        Log.i(TAG, "setting property=" + LatencyTracker.SETTINGS_ENABLED_KEY
-                + ", value=false");
-        latencyTracker.mDeviceConfigPropertiesUpdated.close();
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
-                LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
-        waitForLatencyTrackerToUpdateProperties(latencyTracker);
-        assertThat(latencyTracker.isEnabled(action)).isFalse();
+        assertThat(mLatencyTracker.isEnabled(action)).isTrue();
     }
 
     @Test
     public void testIsEnabledAction_actionPropertyOverridesGlobalProperty()
-            throws DeviceConfig.BadConfigException {
-        LatencyTracker latencyTracker = new LatencyTracker();
+            throws Exception {
         // using a single test action, but this applies to all actions
         int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
-        String actionEnableProperty = "action_show_voice_interaction" + ACTION_ENABLE_SUFFIX;
-        Log.i(TAG, "setting property=" + actionEnableProperty + ", value=true");
+        DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER,
+                LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
+        mLatencyTracker.waitForGlobalEnabledState(false);
 
-        latencyTracker.mDeviceConfigPropertiesUpdated.close();
-        Map<String, String> properties = new HashMap<String, String>() {{
-            put(LatencyTracker.SETTINGS_ENABLED_KEY, "false");
-            put(actionEnableProperty, "true");
-        }};
+        Map<String, String> deviceConfigProperties = new HashMap<>();
+        deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+        deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+        deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "-1");
         DeviceConfig.setProperties(
-                new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
-                        properties));
-        waitForLatencyTrackerToUpdateProperties(latencyTracker);
-        assertThat(latencyTracker.isEnabled(action)).isTrue();
+                new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+                        deviceConfigProperties));
 
-        latencyTracker.mDeviceConfigPropertiesUpdated.close();
-        Log.i(TAG, "setting property=" + actionEnableProperty + ", value=false");
-        properties.put(LatencyTracker.SETTINGS_ENABLED_KEY, "true");
-        properties.put(actionEnableProperty, "false");
-        DeviceConfig.setProperties(
-                    new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
-                            properties));
-        waitForLatencyTrackerToUpdateProperties(latencyTracker);
-        assertThat(latencyTracker.isEnabled(action)).isFalse();
+        mLatencyTracker.waitForMatchingActionProperties(
+                new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+                        -1 /* traceThreshold */));
+
+        assertThat(mLatencyTracker.isEnabled(action)).isTrue();
     }
 
-    private void waitForLatencyTrackerToUpdateProperties(LatencyTracker latencyTracker) {
-        try {
-            Thread.sleep(TEST_TIMEOUT.toMillis());
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-        assertThat(latencyTracker.mDeviceConfigPropertiesUpdated.block(
-                TEST_TIMEOUT.toMillis())).isTrue();
+    @Test
+    public void testLogsWhenEnabled() throws Exception {
+        // using a single test action, but this applies to all actions
+        int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+        Map<String, String> deviceConfigProperties = new HashMap<>();
+        deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+        deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+        deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "-1");
+        DeviceConfig.setProperties(
+                new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+                        deviceConfigProperties));
+        mLatencyTracker.waitForMatchingActionProperties(
+                new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+                        -1 /* traceThreshold */));
+
+        mLatencyTracker.logAction(action, 1234);
+        assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).hasSize(1);
+        LatencyTracker.FrameworkStatsLogEvent frameworkStatsLog =
+                mLatencyTracker.getEventsWrittenToFrameworkStats(action).get(0);
+        assertThat(frameworkStatsLog.logCode).isEqualTo(UI_ACTION_LATENCY_REPORTED);
+        assertThat(frameworkStatsLog.statsdAction).isEqualTo(STATSD_ACTION[action]);
+        assertThat(frameworkStatsLog.durationMillis).isEqualTo(1234);
+
+        mLatencyTracker.clearEvents();
+
+        mLatencyTracker.onActionStart(action);
+        mLatencyTracker.onActionEnd(action);
+        // assert that action was logged, but we cannot confirm duration logged
+        assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).hasSize(1);
+        frameworkStatsLog = mLatencyTracker.getEventsWrittenToFrameworkStats(action).get(0);
+        assertThat(frameworkStatsLog.logCode).isEqualTo(UI_ACTION_LATENCY_REPORTED);
+        assertThat(frameworkStatsLog.statsdAction).isEqualTo(STATSD_ACTION[action]);
     }
 
-    private List<Field> getAllActions() {
-        return Arrays.stream(LatencyTracker.class.getDeclaredFields())
-                .filter(field -> field.getName().startsWith("ACTION_")
-                        && Modifier.isStatic(field.getModifiers())
-                        && field.getType() == int.class)
-                .collect(Collectors.toList());
+    @Test
+    public void testDoesNotLogWhenDisabled() throws Exception {
+        // using a single test action, but this applies to all actions
+        int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+        DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER, "action_show_voice_interaction_enable",
+                "false", false);
+        mLatencyTracker.waitForActionEnabledState(action, false);
+        assertThat(mLatencyTracker.isEnabled(action)).isFalse();
+
+        mLatencyTracker.logAction(action, 1234);
+        assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).isEmpty();
+
+        mLatencyTracker.onActionStart(action);
+        mLatencyTracker.onActionEnd(action);
+        assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).isEmpty();
+    }
+
+    @Test
+    public void testOnActionEndDoesNotLogWithoutOnActionStart()
+            throws Exception {
+        // using a single test action, but this applies to all actions
+        int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+        DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER, "action_show_voice_interaction_enable",
+                "true", false);
+        mLatencyTracker.waitForActionEnabledState(action, true);
+        assertThat(mLatencyTracker.isEnabled(action)).isTrue();
+
+        mLatencyTracker.onActionEnd(action);
+        assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).isEmpty();
+    }
+
+    @Test
+    public void testOnActionEndDoesNotLogWhenCanceled()
+            throws Exception {
+        // using a single test action, but this applies to all actions
+        int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+        DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER, "action_show_voice_interaction_enable",
+                "true", false);
+        mLatencyTracker.waitForActionEnabledState(action, true);
+        assertThat(mLatencyTracker.isEnabled(action)).isTrue();
+
+        mLatencyTracker.onActionStart(action);
+        mLatencyTracker.onActionCancel(action);
+        mLatencyTracker.onActionEnd(action);
+        assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).isEmpty();
+    }
+
+    @Test
+    public void testNeverTriggersPerfettoWhenThresholdNegative()
+            throws Exception {
+        // using a single test action, but this applies to all actions
+        int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+        Map<String, String> deviceConfigProperties = new HashMap<>();
+        deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+        deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+        deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "-1");
+        DeviceConfig.setProperties(
+                new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+                        deviceConfigProperties));
+        mLatencyTracker.waitForMatchingActionProperties(
+                new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+                        -1 /* traceThreshold */));
+
+        mLatencyTracker.onActionStart(action);
+        mLatencyTracker.onActionEnd(action);
+        assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames()).isEmpty();
+    }
+
+    @Test
+    public void testNeverTriggersPerfettoWhenDisabled()
+            throws Exception {
+        // using a single test action, but this applies to all actions
+        int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+        Map<String, String> deviceConfigProperties = new HashMap<>();
+        deviceConfigProperties.put("action_show_voice_interaction_enable", "false");
+        deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+        deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "1");
+        DeviceConfig.setProperties(
+                new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+                        deviceConfigProperties));
+        mLatencyTracker.waitForMatchingActionProperties(
+                new ActionProperties(action, false /* enabled */, 1 /* samplingInterval */,
+                        1 /* traceThreshold */));
+
+        mLatencyTracker.onActionStart(action);
+        mLatencyTracker.onActionEnd(action);
+        assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames()).isEmpty();
+    }
+
+    @Test
+    public void testTriggersPerfettoWhenAboveThreshold()
+            throws Exception {
+        // using a single test action, but this applies to all actions
+        int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+        Map<String, String> deviceConfigProperties = new HashMap<>();
+        deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+        deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+        deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "1");
+        DeviceConfig.setProperties(
+                new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+                        deviceConfigProperties));
+        mLatencyTracker.waitForMatchingActionProperties(
+                new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+                        1 /* traceThreshold */));
+
+        mLatencyTracker.onActionStart(action);
+        // We need to sleep here to ensure that the end call is past the set trace threshold (1ms)
+        Thread.sleep(5 /* millis */);
+        mLatencyTracker.onActionEnd(action);
+        assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames()).hasSize(1);
+        assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames().get(0)).isEqualTo(
+                "com.android.telemetry.latency-tracker-ACTION_SHOW_VOICE_INTERACTION");
+    }
+
+    @Test
+    public void testNeverTriggersPerfettoWhenBelowThreshold()
+            throws Exception {
+        // using a single test action, but this applies to all actions
+        int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+        Map<String, String> deviceConfigProperties = new HashMap<>();
+        deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+        deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+        deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "1000");
+        DeviceConfig.setProperties(
+                new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+                        deviceConfigProperties));
+        mLatencyTracker.waitForMatchingActionProperties(
+                new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+                        1000 /* traceThreshold */));
+
+        mLatencyTracker.onActionStart(action);
+        // No sleep here to ensure that end call comes before 1000ms threshold
+        mLatencyTracker.onActionEnd(action);
+        assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames()).isEmpty();
+    }
+
+    private List<Field> getAllActionFields() {
+        return Arrays.stream(LatencyTracker.class.getDeclaredFields()).filter(
+                field -> field.getName().startsWith("ACTION_") && Modifier.isStatic(
+                        field.getModifiers()) && field.getType() == int.class).collect(
+                Collectors.toList());
     }
 
     private int getIntFieldChecked(Field field) {
diff --git a/core/tests/coretests/src/com/android/internal/util/OWNERS b/core/tests/coretests/src/com/android/internal/util/OWNERS
index d832745..dda11fb 100644
--- a/core/tests/coretests/src/com/android/internal/util/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/util/OWNERS
@@ -1,2 +1,3 @@
 per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
-per-file *ContrastColor* = file:/services/core/java/com/android/server/notification/OWNERS
\ No newline at end of file
+per-file *ContrastColor* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *LatencyTracker* = file:/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
diff --git a/core/tests/coretests/testdoubles/Android.bp b/core/tests/coretests/testdoubles/Android.bp
new file mode 100644
index 0000000..35f6911
--- /dev/null
+++ b/core/tests/coretests/testdoubles/Android.bp
@@ -0,0 +1,19 @@
+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
+    //   SPDX-license-identifier-BSD
+    //   legacy_unencumbered
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "FrameworksCoreTestDoubles-sources",
+    srcs: ["src/**/*.java"],
+    visibility: [
+        "//frameworks/base/core/tests/coretests",
+        "//frameworks/base/services/tests/voiceinteractiontests",
+    ],
+}
diff --git a/core/tests/coretests/testdoubles/OWNERS b/core/tests/coretests/testdoubles/OWNERS
new file mode 100644
index 0000000..baf92ec
--- /dev/null
+++ b/core/tests/coretests/testdoubles/OWNERS
@@ -0,0 +1 @@
+per-file *LatencyTracker* = file:/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
diff --git a/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java b/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java
new file mode 100644
index 0000000..306ecde
--- /dev/null
+++ b/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.LatencyTracker.ActionProperties.ENABLE_SUFFIX;
+import static com.android.internal.util.LatencyTracker.ActionProperties.SAMPLE_INTERVAL_SUFFIX;
+import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.ConditionVariable;
+import android.provider.DeviceConfig;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+public final class FakeLatencyTracker extends LatencyTracker {
+
+    private static final String TAG = "FakeLatencyTracker";
+    private static final Duration FORCE_UPDATE_TIMEOUT = Duration.ofSeconds(1);
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private final Map<Integer, List<FrameworkStatsLogEvent>> mLatenciesLogged;
+    @GuardedBy("mLock")
+    private final List<String> mPerfettoTraceNamesTriggered;
+    private final AtomicReference<SparseArray<ActionProperties>> mLastPropertiesUpdate =
+            new AtomicReference<>();
+    @Nullable
+    @GuardedBy("mLock")
+    private Callable<Boolean> mShouldClosePropertiesUpdatedCallable = null;
+    private final ConditionVariable mDeviceConfigPropertiesUpdated = new ConditionVariable();
+
+    public static FakeLatencyTracker create() throws Exception {
+        Log.i(TAG, "create");
+        disableForAllActions();
+        FakeLatencyTracker fakeLatencyTracker = new FakeLatencyTracker();
+        // always return the fake in the disabled state and let the client control the desired state
+        fakeLatencyTracker.waitForGlobalEnabledState(false);
+        fakeLatencyTracker.waitForAllPropertiesEnableState(false);
+        return fakeLatencyTracker;
+    }
+
+    FakeLatencyTracker() {
+        super();
+        mLatenciesLogged = new HashMap<>();
+        mPerfettoTraceNamesTriggered = new ArrayList<>();
+    }
+
+    private static void disableForAllActions() throws DeviceConfig.BadConfigException {
+        Map<String, String> properties = new HashMap<>();
+        properties.put(LatencyTracker.SETTINGS_ENABLED_KEY, "false");
+        for (int action : STATSD_ACTION) {
+            Log.d(TAG, "disabling action=" + action + ", property=" + getNameOfAction(
+                    action).toLowerCase(Locale.ROOT) + ENABLE_SUFFIX);
+            properties.put(getNameOfAction(action).toLowerCase(Locale.ROOT) + ENABLE_SUFFIX,
+                    "false");
+        }
+
+        DeviceConfig.setProperties(
+                new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER, properties));
+    }
+
+    public void forceEnabled(int action, int traceThresholdMillis)
+            throws Exception {
+        String actionName = getNameOfAction(STATSD_ACTION[action]).toLowerCase(Locale.ROOT);
+        String actionEnableProperty = actionName + ENABLE_SUFFIX;
+        String actionSampleProperty = actionName + SAMPLE_INTERVAL_SUFFIX;
+        String actionTraceProperty = actionName + TRACE_THRESHOLD_SUFFIX;
+        Log.i(TAG, "setting property=" + actionTraceProperty + ", value=" + traceThresholdMillis);
+        Log.i(TAG, "setting property=" + actionEnableProperty + ", value=true");
+
+        Map<String, String> properties = new HashMap<>(ImmutableMap.of(
+                actionEnableProperty, "true",
+                // Fake forces to sample every event
+                actionSampleProperty, String.valueOf(1),
+                actionTraceProperty, String.valueOf(traceThresholdMillis)
+        ));
+        DeviceConfig.setProperties(
+                new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER, properties));
+        waitForMatchingActionProperties(
+                new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+                        traceThresholdMillis));
+    }
+
+    public List<FrameworkStatsLogEvent> getEventsWrittenToFrameworkStats(@Action int action) {
+        synchronized (mLock) {
+            Log.i(TAG, "getEventsWrittenToFrameworkStats: mLatenciesLogged=" + mLatenciesLogged);
+            return mLatenciesLogged.getOrDefault(action, Collections.emptyList());
+        }
+    }
+
+    public List<String> getTriggeredPerfettoTraceNames() {
+        synchronized (mLock) {
+            return mPerfettoTraceNamesTriggered;
+        }
+    }
+
+    public void clearEvents() {
+        synchronized (mLock) {
+            mLatenciesLogged.clear();
+            mPerfettoTraceNamesTriggered.clear();
+        }
+    }
+
+    @Override
+    public void onDeviceConfigPropertiesUpdated(SparseArray<ActionProperties> actionProperties) {
+        Log.d(TAG, "onDeviceConfigPropertiesUpdated: " + actionProperties);
+        mLastPropertiesUpdate.set(actionProperties);
+        synchronized (mLock) {
+            if (mShouldClosePropertiesUpdatedCallable != null) {
+                try {
+                    boolean shouldClosePropertiesUpdated =
+                            mShouldClosePropertiesUpdatedCallable.call();
+                    Log.i(TAG, "shouldClosePropertiesUpdatedCallable callable result="
+                            + shouldClosePropertiesUpdated);
+                    if (shouldClosePropertiesUpdated) {
+                        Log.i(TAG, "shouldClosePropertiesUpdatedCallable=true, opening condition");
+                        mShouldClosePropertiesUpdatedCallable = null;
+                        mDeviceConfigPropertiesUpdated.open();
+                    }
+                } catch (Exception e) {
+                    Log.e(TAG, "exception when calling callable", e);
+                    throw new RuntimeException(e);
+                }
+            } else {
+                Log.i(TAG, "no conditional callable set, opening condition");
+                mDeviceConfigPropertiesUpdated.open();
+            }
+        }
+    }
+
+    @Override
+    public void onTriggerPerfetto(String triggerName) {
+        synchronized (mLock) {
+            mPerfettoTraceNamesTriggered.add(triggerName);
+        }
+    }
+
+    @Override
+    public void onLogToFrameworkStats(FrameworkStatsLogEvent event) {
+        synchronized (mLock) {
+            Log.i(TAG, "onLogToFrameworkStats: event=" + event);
+            List<FrameworkStatsLogEvent> eventList = mLatenciesLogged.getOrDefault(event.action,
+                    new ArrayList<>());
+            eventList.add(event);
+            mLatenciesLogged.put(event.action, eventList);
+        }
+    }
+
+    public void waitForAllPropertiesEnableState(boolean enabledState) throws Exception {
+        Log.i(TAG, "waitForAllPropertiesEnableState: enabledState=" + enabledState);
+        synchronized (mLock) {
+            Log.i(TAG, "closing condition");
+            mDeviceConfigPropertiesUpdated.close();
+            // Update the callable to only close the properties updated condition when all the
+            // desired properties have been updated. The DeviceConfig callbacks may happen multiple
+            // times so testing the resulting updates is required.
+            mShouldClosePropertiesUpdatedCallable = () -> {
+                Log.i(TAG, "verifying if last properties update has all properties enable="
+                        + enabledState);
+                SparseArray<ActionProperties> newProperties = mLastPropertiesUpdate.get();
+                if (newProperties != null) {
+                    for (int i = 0; i < newProperties.size(); i++) {
+                        if (newProperties.get(i).isEnabled() != enabledState) {
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            };
+            if (mShouldClosePropertiesUpdatedCallable.call()) {
+                return;
+            }
+        }
+        Log.i(TAG, "waiting for condition");
+        assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+    }
+
+    public void waitForMatchingActionProperties(ActionProperties actionProperties)
+            throws Exception {
+        Log.i(TAG, "waitForMatchingActionProperties: actionProperties=" + actionProperties);
+        synchronized (mLock) {
+            Log.i(TAG, "closing condition");
+            mDeviceConfigPropertiesUpdated.close();
+            // Update the callable to only close the properties updated condition when all the
+            // desired properties have been updated. The DeviceConfig callbacks may happen multiple
+            // times so testing the resulting updates is required.
+            mShouldClosePropertiesUpdatedCallable = () -> {
+                Log.i(TAG, "verifying if last properties update contains matching property ="
+                        + actionProperties);
+                SparseArray<ActionProperties> newProperties = mLastPropertiesUpdate.get();
+                if (newProperties != null) {
+                    if (newProperties.size() > 0) {
+                        return newProperties.get(actionProperties.getAction()).equals(
+                                actionProperties);
+                    }
+                }
+                return false;
+            };
+            if (mShouldClosePropertiesUpdatedCallable.call()) {
+                return;
+            }
+        }
+        Log.i(TAG, "waiting for condition");
+        assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+    }
+
+    public void waitForActionEnabledState(int action, boolean enabledState) throws Exception {
+        Log.i(TAG, "waitForActionEnabledState:"
+                + " action=" + action + ", enabledState=" + enabledState);
+        synchronized (mLock) {
+            Log.i(TAG, "closing condition");
+            mDeviceConfigPropertiesUpdated.close();
+            // Update the callable to only close the properties updated condition when all the
+            // desired properties have been updated. The DeviceConfig callbacks may happen multiple
+            // times so testing the resulting updates is required.
+            mShouldClosePropertiesUpdatedCallable = () -> {
+                Log.i(TAG, "verifying if last properties update contains action=" + action
+                        + ", enabledState=" + enabledState);
+                SparseArray<ActionProperties> newProperties = mLastPropertiesUpdate.get();
+                if (newProperties != null) {
+                    if (newProperties.size() > 0) {
+                        return newProperties.get(action).isEnabled() == enabledState;
+                    }
+                }
+                return false;
+            };
+            if (mShouldClosePropertiesUpdatedCallable.call()) {
+                return;
+            }
+        }
+        Log.i(TAG, "waiting for condition");
+        assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+    }
+
+    public void waitForGlobalEnabledState(boolean enabledState) throws Exception {
+        Log.i(TAG, "waitForGlobalEnabledState: enabledState=" + enabledState);
+        synchronized (mLock) {
+            Log.i(TAG, "closing condition");
+            mDeviceConfigPropertiesUpdated.close();
+            // Update the callable to only close the properties updated condition when all the
+            // desired properties have been updated. The DeviceConfig callbacks may happen multiple
+            // times so testing the resulting updates is required.
+            mShouldClosePropertiesUpdatedCallable = () -> {
+                //noinspection deprecation
+                return isEnabled() == enabledState;
+            };
+            if (mShouldClosePropertiesUpdatedCallable.call()) {
+                return;
+            }
+        }
+        Log.i(TAG, "waiting for condition");
+        assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+    }
+}
diff --git a/libs/WindowManager/Shell/res/color/unfold_background.xml b/libs/WindowManager/Shell/res/color/unfold_background.xml
new file mode 100644
index 0000000..e33eb12
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/unfold_background.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_500" android:lStar="5" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e24c228..85a353f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -206,12 +206,14 @@
 
     public Bubble(Intent intent,
             UserHandle user,
+            @Nullable Icon icon,
             Executor mainExecutor) {
         mKey = KEY_APP_BUBBLE;
         mGroupKey = null;
         mLocusId = null;
         mFlags = 0;
         mUser = user;
+        mIcon = icon;
         mShowBubbleUpdateDot = false;
         mMainExecutor = mainExecutor;
         mTaskId = INVALID_TASK_ID;
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 48fe65d..d2889e7 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
@@ -57,6 +57,7 @@
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.drawable.Icon;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -1034,8 +1035,9 @@
      *
      * @param intent the intent to display in the bubble expanded view.
      * @param user the {@link UserHandle} of the user to start this activity for.
+     * @param icon the {@link Icon} to use for the bubble view.
      */
-    public void showOrHideAppBubble(Intent intent, UserHandle user) {
+    public void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon) {
         if (intent == null || intent.getPackage() == null) {
             Log.w(TAG, "App bubble failed to show, invalid intent: " + intent
                     + ((intent != null) ? " with package: " + intent.getPackage() : " "));
@@ -1063,7 +1065,7 @@
             }
         } else {
             // App bubble does not exist, lets add and expand it
-            Bubble b = new Bubble(intent, user, mMainExecutor);
+            Bubble b = new Bubble(intent, user, icon, mMainExecutor);
             b.setShouldAutoExpand(true);
             inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
         }
@@ -1871,9 +1873,9 @@
         }
 
         @Override
-        public void showOrHideAppBubble(Intent intent, UserHandle user) {
+        public void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon) {
             mMainExecutor.execute(
-                    () -> BubbleController.this.showOrHideAppBubble(intent, user));
+                    () -> BubbleController.this.showOrHideAppBubble(intent, user, icon));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 5555bec..876a720 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -26,6 +26,7 @@
 import android.app.NotificationChannel;
 import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.graphics.drawable.Icon;
 import android.hardware.HardwareBuffer;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
@@ -135,8 +136,9 @@
      *
      * @param intent the intent to display in the bubble expanded view.
      * @param user the {@link UserHandle} of the user to start this activity for.
+     * @param icon the {@link Icon} to use for the bubble view.
      */
-    void showOrHideAppBubble(Intent intent, UserHandle user);
+    void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon);
 
     /** @return true if the specified {@code taskId} corresponds to app bubble's taskId. */
     boolean isAppBubbleTaskId(int taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 57b5b8f..3d5230d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -82,7 +82,6 @@
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.recents.RecentsTransitionHandler;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.startingsurface.StartingSurface;
@@ -521,9 +520,6 @@
                         desktopModeTaskRepository, mainExecutor));
     }
 
-    @BindsOptionalOf
-    abstract RecentsTransitionHandler optionalRecentsTransitionHandler();
-
     //
     // Shell transitions
     //
@@ -807,7 +803,6 @@
             Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
             Optional<FreeformComponents> freeformComponents,
             Optional<RecentTasksController> recentTasksOptional,
-            Optional<RecentsTransitionHandler> recentsTransitionHandlerOptional,
             Optional<OneHandedController> oneHandedControllerOptional,
             Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
             Optional<ActivityEmbeddingController> activityEmbeddingOptional,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index cc0da28..7a83d10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -83,7 +83,6 @@
 import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.recents.RecentsTransitionHandler;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
@@ -529,20 +528,9 @@
             ShellInit shellInit,
             Optional<SplitScreenController> splitScreenOptional,
             Optional<PipTouchHandler> pipTouchHandlerOptional,
-            Optional<RecentsTransitionHandler> recentsTransitionHandler,
             Transitions transitions) {
         return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
-                pipTouchHandlerOptional, recentsTransitionHandler);
-    }
-
-    @WMSingleton
-    @Provides
-    static RecentsTransitionHandler provideRecentsTransitionHandler(
-            ShellInit shellInit,
-            Transitions transitions,
-            Optional<RecentTasksController> recentTasksController) {
-        return new RecentsTransitionHandler(shellInit, transitions,
-                recentTasksController.orElse(null));
+                pipTouchHandlerOptional);
     }
 
     //
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 52f5a8c..c19d543 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
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -536,6 +537,15 @@
             mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
             return;
         }
+        if (mSplitScreenOptional.isPresent()) {
+            // If pip activity will reparent to origin task case and if the origin task still under
+            // split root, just exit split screen here to ensure it could expand to fullscreen.
+            SplitScreenController split = mSplitScreenOptional.get();
+            if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) {
+                split.exitSplitScreen(INVALID_TASK_ID,
+                        SplitScreenController.EXIT_REASON_APP_FINISHED);
+            }
+        }
         mSyncTransactionQueue.queue(wct);
         mSyncTransactionQueue.runInSync(t -> {
             // Make sure to grab the latest source hint rect as it could have been
@@ -1479,9 +1489,13 @@
                 applyFinishBoundsResize(wct, direction, false);
             }
         } else {
-            final boolean isPipTopLeft =
-                    direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && isPipToTopLeft();
-            applyFinishBoundsResize(wct, direction, isPipTopLeft);
+            applyFinishBoundsResize(wct, direction, isPipToTopLeft());
+            // Use sync transaction to apply finish transaction for enter split case.
+            if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+                mSyncTransactionQueue.runInSync(t -> {
+                    t.merge(tx);
+                });
+            }
         }
 
         finishResizeForMenu(destinationBounds);
@@ -1518,7 +1532,10 @@
         mSurfaceTransactionHelper.round(tx, mLeash, isInPip());
 
         wct.setBounds(mToken, taskBounds);
-        wct.setBoundsChangeTransaction(mToken, tx);
+        // Pip to split should use sync transaction to sync split bounds change.
+        if (direction != TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+            wct.setBoundsChangeTransaction(mToken, tx);
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 979b7c7..167c032 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -282,7 +282,8 @@
 
     public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
         final boolean isSplitScreen = mSplitScreenControllerOptional.isPresent()
-                && mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId);
+                && mSplitScreenControllerOptional.get().isTaskInSplitScreenForeground(
+                taskInfo.taskId);
         mFocusedTaskAllowSplitScreen = isSplitScreen
                 || (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                 && taskInfo.supportsMultiWindow
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
index 4048c5b..1a6c1d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
@@ -17,11 +17,6 @@
 package com.android.wm.shell.recents;
 
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.IApplicationThread;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.IRecentsAnimationRunner;
 
 import com.android.wm.shell.recents.IRecentTasksListener;
 import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -50,10 +45,4 @@
      * Gets the set of running tasks.
      */
     RunningTaskInfo[] getRunningTasks(int maxNum) = 4;
-
-    /**
-     * Starts a recents transition.
-     */
-    oneway void startRecentsTransition(in PendingIntent intent, in Intent fillIn, in Bundle options,
-                    IApplicationThread appThread, IRecentsAnimationRunner listener) = 5;
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index c5bfd87..0d9faa3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -24,18 +24,13 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
-import android.app.IApplicationThread;
-import android.app.PendingIntent;
 import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
-import android.view.IRecentsAnimationRunner;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.NonNull;
@@ -84,7 +79,6 @@
     private final TaskStackListenerImpl mTaskStackListener;
     private final RecentTasksImpl mImpl = new RecentTasksImpl();
     private final ActivityTaskManager mActivityTaskManager;
-    private RecentsTransitionHandler mTransitionHandler = null;
     private IRecentTasksListener mListener;
     private final boolean mIsDesktopMode;
 
@@ -156,10 +150,6 @@
         mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTaskListener(this));
     }
 
-    void setTransitionHandler(RecentsTransitionHandler handler) {
-        mTransitionHandler = handler;
-    }
-
     /**
      * Adds a split pair. This call does not validate the taskIds, only that they are not the same.
      */
@@ -502,18 +492,5 @@
                     true /* blocking */);
             return tasks[0];
         }
-
-        @Override
-        public void startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
-                IApplicationThread appThread, IRecentsAnimationRunner listener) {
-            if (mController.mTransitionHandler == null) {
-                Slog.e(TAG, "Used shell-transitions startRecentsTransition without"
-                        + " shell-transitions");
-                return;
-            }
-            executeRemoteCallWithTaskPermission(mController, "startRecentsTransition",
-                    (controller) -> controller.mTransitionHandler.startRecentsTransition(
-                            intent, fillIn, options, appThread, listener));
-        }
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
deleted file mode 100644
index da8c805..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ /dev/null
@@ -1,759 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.recents;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
-import static android.view.WindowManager.TRANSIT_SLEEP;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
-
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.IApplicationThread;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.view.IRecentsAnimationController;
-import android.view.IRecentsAnimationRunner;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.window.PictureInPictureSurfaceTransaction;
-import android.window.TaskSnapshot;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.util.TransitionUtil;
-
-import java.util.ArrayList;
-
-/**
- * Handles the Recents (overview) animation. Only one of these can run at a time. A recents
- * transition must be created via {@link #startRecentsTransition}. Anything else will be ignored.
- */
-public class RecentsTransitionHandler implements Transitions.TransitionHandler {
-    private static final String TAG = "RecentsTransitionHandler";
-
-    private final Transitions mTransitions;
-    private final ShellExecutor mExecutor;
-    private IApplicationThread mAnimApp = null;
-    private final ArrayList<RecentsController> mControllers = new ArrayList<>();
-
-    /**
-     * List of other handlers which might need to mix recents with other things. These are checked
-     * in the order they are added. Ideally there should only be one.
-     */
-    private final ArrayList<RecentsMixedHandler> mMixers = new ArrayList<>();
-
-    public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,
-            @Nullable RecentTasksController recentTasksController) {
-        mTransitions = transitions;
-        mExecutor = transitions.getMainExecutor();
-        if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;
-        if (recentTasksController == null) return;
-        shellInit.addInitCallback(() -> {
-            recentTasksController.setTransitionHandler(this);
-            transitions.addHandler(this);
-        }, this);
-    }
-
-    /** Register a mixer handler. {@see RecentsMixedHandler}*/
-    public void addMixer(RecentsMixedHandler mixer) {
-        mMixers.add(mixer);
-    }
-
-    /** Unregister a Mixed Handler */
-    public void removeMixer(RecentsMixedHandler mixer) {
-        mMixers.remove(mixer);
-    }
-
-    void startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
-            IApplicationThread appThread, IRecentsAnimationRunner listener) {
-        // only care about latest one.
-        mAnimApp = appThread;
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.sendPendingIntent(intent, fillIn, options);
-        final RecentsController controller = new RecentsController(listener);
-        RecentsMixedHandler mixer = null;
-        Transitions.TransitionHandler mixedHandler = null;
-        for (int i = 0; i < mMixers.size(); ++i) {
-            mixedHandler = mMixers.get(i).handleRecentsRequest(wct);
-            if (mixedHandler != null) {
-                mixer = mMixers.get(i);
-                break;
-            }
-        }
-        final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
-                mixedHandler == null ? this : mixedHandler);
-        if (mixer != null) {
-            mixer.setRecentsTransition(transition);
-        }
-        if (transition == null) {
-            controller.cancel();
-            return;
-        }
-        controller.setTransition(transition);
-        mControllers.add(controller);
-    }
-
-    @Override
-    public WindowContainerTransaction handleRequest(IBinder transition,
-            TransitionRequestInfo request) {
-        // do not directly handle requests. Only entry point should be via startRecentsTransition
-        return null;
-    }
-
-    private int findController(IBinder transition) {
-        for (int i = mControllers.size() - 1; i >= 0; --i) {
-            if (mControllers.get(i).mTransition == transition) return i;
-        }
-        return -1;
-    }
-
-    @Override
-    public boolean startAnimation(IBinder transition, TransitionInfo info,
-            SurfaceControl.Transaction startTransaction,
-            SurfaceControl.Transaction finishTransaction,
-            Transitions.TransitionFinishCallback finishCallback) {
-        final int controllerIdx = findController(transition);
-        if (controllerIdx < 0) return false;
-        final RecentsController controller = mControllers.get(controllerIdx);
-        Transitions.setRunningRemoteTransitionDelegate(mAnimApp);
-        mAnimApp = null;
-        if (!controller.start(info, startTransaction, finishTransaction, finishCallback)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public void mergeAnimation(IBinder transition, TransitionInfo info,
-            SurfaceControl.Transaction t, IBinder mergeTarget,
-            Transitions.TransitionFinishCallback finishCallback) {
-        final int targetIdx = findController(mergeTarget);
-        if (targetIdx < 0) return;
-        final RecentsController controller = mControllers.get(targetIdx);
-        controller.merge(info, t, finishCallback);
-    }
-
-    @Override
-    public void onTransitionConsumed(IBinder transition, boolean aborted,
-            SurfaceControl.Transaction finishTransaction) {
-        final int idx = findController(transition);
-        if (idx < 0) return;
-        mControllers.get(idx).cancel();
-    }
-
-    /** There is only one of these and it gets reset on finish. */
-    private class RecentsController extends IRecentsAnimationController.Stub {
-        private IRecentsAnimationRunner mListener;
-        private IBinder.DeathRecipient mDeathHandler;
-        private Transitions.TransitionFinishCallback mFinishCB = null;
-        private SurfaceControl.Transaction mFinishTransaction = null;
-
-        /**
-         * List of tasks that we are switching away from via this transition. Upon finish, these
-         * pausing tasks will become invisible.
-         * These need to be ordered since the order must be restored if there is no task-switch.
-         */
-        private ArrayList<TaskState> mPausingTasks = null;
-
-        /**
-         * List of tasks that we are switching to. Upon finish, these will remain visible and
-         * on top.
-         */
-        private ArrayList<TaskState> mOpeningTasks = null;
-
-        private WindowContainerToken mPipTask = null;
-        private WindowContainerToken mRecentsTask = null;
-        private int mRecentsTaskId = -1;
-        private TransitionInfo mInfo = null;
-        private boolean mOpeningSeparateHome = false;
-        private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
-        private PictureInPictureSurfaceTransaction mPipTransaction = null;
-        private IBinder mTransition = null;
-        private boolean mKeyguardLocked = false;
-        private boolean mWillFinishToHome = false;
-
-        /** The animation is idle, waiting for the user to choose a task to switch to. */
-        private static final int STATE_NORMAL = 0;
-
-        /** The user chose a new task to switch to and the animation is animating to it. */
-        private static final int STATE_NEW_TASK = 1;
-
-        /** The latest state that the recents animation is operating in. */
-        private int mState = STATE_NORMAL;
-
-        RecentsController(IRecentsAnimationRunner listener) {
-            mListener = listener;
-            mDeathHandler = () -> mExecutor.execute(() -> {
-                if (mListener == null) return;
-                if (mFinishCB != null) {
-                    finish(mWillFinishToHome, false /* leaveHint */);
-                }
-            });
-            try {
-                mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */);
-            } catch (RemoteException e) {
-                mListener = null;
-            }
-        }
-
-        void setTransition(IBinder transition) {
-            mTransition = transition;
-        }
-
-        void cancel() {
-            // restoring (to-home = false) involves submitting more WM changes, so by default, use
-            // toHome = true when canceling.
-            cancel(true /* toHome */);
-        }
-
-        void cancel(boolean toHome) {
-            if (mFinishCB != null && mListener != null) {
-                try {
-                    mListener.onAnimationCanceled(null, null);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Error canceling recents animation", e);
-                }
-                finish(toHome, false /* userLeave */);
-            } else {
-                cleanUp();
-            }
-        }
-
-        /**
-         * Sends a cancel message to the recents animation with snapshots. Used to trigger a
-         * "replace-with-screenshot" like behavior.
-         */
-        private boolean sendCancelWithSnapshots() {
-            int[] taskIds = null;
-            TaskSnapshot[] snapshots = null;
-            if (mPausingTasks.size() > 0) {
-                taskIds = new int[mPausingTasks.size()];
-                snapshots = new TaskSnapshot[mPausingTasks.size()];
-                try {
-                    for (int i = 0; i < mPausingTasks.size(); ++i) {
-                        snapshots[i] = ActivityTaskManager.getService().takeTaskSnapshot(
-                                mPausingTasks.get(0).mTaskInfo.taskId);
-                    }
-                } catch (RemoteException e) {
-                    taskIds = null;
-                    snapshots = null;
-                }
-            }
-            try {
-                mListener.onAnimationCanceled(taskIds, snapshots);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Error canceling recents animation", e);
-                return false;
-            }
-            return true;
-        }
-
-        void cleanUp() {
-            if (mListener != null && mDeathHandler != null) {
-                mListener.asBinder().unlinkToDeath(mDeathHandler, 0 /* flags */);
-                mDeathHandler = null;
-            }
-            mListener = null;
-            mFinishCB = null;
-            // clean-up leash surfacecontrols and anything that might reference them.
-            if (mLeashMap != null) {
-                for (int i = 0; i < mLeashMap.size(); ++i) {
-                    mLeashMap.valueAt(i).release();
-                }
-                mLeashMap = null;
-            }
-            mFinishTransaction = null;
-            mPausingTasks = null;
-            mOpeningTasks = null;
-            mInfo = null;
-            mTransition = null;
-            mControllers.remove(this);
-        }
-
-        boolean start(TransitionInfo info, SurfaceControl.Transaction t,
-                SurfaceControl.Transaction finishT, Transitions.TransitionFinishCallback finishCB) {
-            if (mListener == null || mTransition == null) {
-                cleanUp();
-                return false;
-            }
-            // First see if this is a valid recents transition.
-            boolean hasPausingTasks = false;
-            for (int i = 0; i < info.getChanges().size(); ++i) {
-                final TransitionInfo.Change change = info.getChanges().get(i);
-                if (TransitionUtil.isWallpaper(change)) continue;
-                if (TransitionUtil.isClosingType(change.getMode())) {
-                    hasPausingTasks = true;
-                    continue;
-                }
-                final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-                if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) {
-                    mRecentsTask = taskInfo.token;
-                    mRecentsTaskId = taskInfo.taskId;
-                } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
-                    mRecentsTask = taskInfo.token;
-                    mRecentsTaskId = taskInfo.taskId;
-                }
-            }
-            if (mRecentsTask == null || !hasPausingTasks) {
-                // Recents is already running apparently, so this is a no-op.
-                Slog.e(TAG, "Tried to start recents while it is already running. recents="
-                        + mRecentsTask);
-                cleanUp();
-                return false;
-            }
-
-            mInfo = info;
-            mFinishCB = finishCB;
-            mFinishTransaction = finishT;
-            mPausingTasks = new ArrayList<>();
-            mOpeningTasks = new ArrayList<>();
-            mLeashMap = new ArrayMap<>();
-            mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
-
-            final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
-            final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
-            TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter();
-            // About layering: we divide up the "layer space" into 3 regions (each the size of
-            // the change count). This lets us categorize things into above/below/between
-            // while maintaining their relative ordering.
-            for (int i = 0; i < info.getChanges().size(); ++i) {
-                final TransitionInfo.Change change = info.getChanges().get(i);
-                final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-                if (TransitionUtil.isWallpaper(change)) {
-                    final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
-                            // wallpapers go into the "below" layer space
-                            info.getChanges().size() - i, info, t, mLeashMap);
-                    wallpapers.add(target);
-                    // Make all the wallpapers opaque since we want them visible from the start
-                    t.setAlpha(target.leash, 1);
-                } else if (leafTaskFilter.test(change)) {
-                    // start by putting everything into the "below" layer space.
-                    final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
-                            info.getChanges().size() - i, info, t, mLeashMap);
-                    apps.add(target);
-                    if (TransitionUtil.isClosingType(change.getMode())) {
-                        // raise closing (pausing) task to "above" layer so it isn't covered
-                        t.setLayer(target.leash, info.getChanges().size() * 3 - i);
-                        mPausingTasks.add(new TaskState(change, target.leash));
-                        if (taskInfo.pictureInPictureParams != null
-                                && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
-                            mPipTask = taskInfo.token;
-                        }
-                    } else if (taskInfo != null
-                            && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) {
-                        // There's a 3p launcher, so make sure recents goes above that.
-                        t.setLayer(target.leash, info.getChanges().size() * 3 - i);
-                    } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
-                        // do nothing
-                    } else if (TransitionUtil.isOpeningType(change.getMode())) {
-                        mOpeningTasks.add(new TaskState(change, target.leash));
-                    }
-                }
-            }
-            t.apply();
-            try {
-                mListener.onAnimationStart(this,
-                        apps.toArray(new RemoteAnimationTarget[apps.size()]),
-                        wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
-                        new Rect(0, 0, 0, 0), new Rect());
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Error starting recents animation", e);
-                cancel();
-            }
-            return true;
-        }
-
-        @SuppressLint("NewApi")
-        void merge(TransitionInfo info, SurfaceControl.Transaction t,
-                Transitions.TransitionFinishCallback finishCallback) {
-            if (mFinishCB == null) {
-                // This was no-op'd (likely a repeated start) and we've already sent finish.
-                return;
-            }
-            if (info.getType() == TRANSIT_SLEEP) {
-                // A sleep event means we need to stop animations immediately, so cancel here.
-                cancel();
-                return;
-            }
-            ArrayList<TransitionInfo.Change> openingTasks = null;
-            ArrayList<TransitionInfo.Change> closingTasks = null;
-            mOpeningSeparateHome = false;
-            TransitionInfo.Change recentsOpening = null;
-            boolean foundRecentsClosing = false;
-            boolean hasChangingApp = false;
-            final TransitionUtil.LeafTaskFilter leafTaskFilter =
-                    new TransitionUtil.LeafTaskFilter();
-            for (int i = 0; i < info.getChanges().size(); ++i) {
-                final TransitionInfo.Change change = info.getChanges().get(i);
-                final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-                final boolean isLeafTask = leafTaskFilter.test(change);
-                if (TransitionUtil.isOpeningType(change.getMode())) {
-                    if (mRecentsTask.equals(change.getContainer())) {
-                        recentsOpening = change;
-                    } else if (isLeafTask) {
-                        if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
-                            // This is usually a 3p launcher
-                            mOpeningSeparateHome = true;
-                        }
-                        if (openingTasks == null) {
-                            openingTasks = new ArrayList<>();
-                        }
-                        openingTasks.add(change);
-                    }
-                } else if (TransitionUtil.isClosingType(change.getMode())) {
-                    if (mRecentsTask.equals(change.getContainer())) {
-                        foundRecentsClosing = true;
-                    } else if (isLeafTask) {
-                        if (closingTasks == null) {
-                            closingTasks = new ArrayList<>();
-                        }
-                        closingTasks.add(change);
-                    }
-                } else if (change.getMode() == TRANSIT_CHANGE) {
-                    // Finish recents animation if the display is changed, so the default
-                    // transition handler can play the animation such as rotation effect.
-                    if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) {
-                        cancel(mWillFinishToHome);
-                        return;
-                    }
-                    hasChangingApp = true;
-                }
-            }
-            if (hasChangingApp && foundRecentsClosing) {
-                // This happens when a visible app is expanding (usually PiP). In this case,
-                // that transition probably has a special-purpose animation, so finish recents
-                // now and let it do its animation (since recents is going to be occluded).
-                sendCancelWithSnapshots();
-                mExecutor.executeDelayed(
-                        () -> finishInner(true /* toHome */, false /* userLeaveHint */), 0);
-                return;
-            }
-            if (recentsOpening != null) {
-                // the recents task re-appeared. This happens if the user gestures before the
-                // task-switch (NEW_TASK) animation finishes.
-                if (mState == STATE_NORMAL) {
-                    Slog.e(TAG, "Returning to recents while recents is already idle.");
-                }
-                if (closingTasks == null || closingTasks.size() == 0) {
-                    Slog.e(TAG, "Returning to recents without closing any opening tasks.");
-                }
-                // Setup may hide it initially since it doesn't know that overview was still active.
-                t.show(recentsOpening.getLeash());
-                t.setAlpha(recentsOpening.getLeash(), 1.f);
-                mState = STATE_NORMAL;
-            }
-            boolean didMergeThings = false;
-            if (closingTasks != null) {
-                // Cancelling a task-switch. Move the tasks back to mPausing from mOpening
-                for (int i = 0; i < closingTasks.size(); ++i) {
-                    final TransitionInfo.Change change = closingTasks.get(i);
-                    int openingIdx = TaskState.indexOf(mOpeningTasks, change);
-                    if (openingIdx < 0) {
-                        Slog.e(TAG, "Back to existing recents animation from an unrecognized "
-                                + "task: " + change.getTaskInfo().taskId);
-                        continue;
-                    }
-                    mPausingTasks.add(mOpeningTasks.remove(openingIdx));
-                    didMergeThings = true;
-                }
-            }
-            RemoteAnimationTarget[] appearedTargets = null;
-            if (openingTasks != null && openingTasks.size() > 0) {
-                // Switching to some new tasks, add to mOpening and remove from mPausing. Also,
-                // enter NEW_TASK state since this will start the switch-to animation.
-                final int layer = mInfo.getChanges().size() * 3;
-                appearedTargets = new RemoteAnimationTarget[openingTasks.size()];
-                for (int i = 0; i < openingTasks.size(); ++i) {
-                    final TransitionInfo.Change change = openingTasks.get(i);
-                    int pausingIdx = TaskState.indexOf(mPausingTasks, change);
-                    if (pausingIdx >= 0) {
-                        // Something is showing/opening a previously-pausing app.
-                        appearedTargets[i] = TransitionUtil.newTarget(
-                                change, layer, mPausingTasks.get(pausingIdx).mLeash);
-                        mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
-                        // Setup hides opening tasks initially, so make it visible again (since we
-                        // are already showing it).
-                        t.show(change.getLeash());
-                        t.setAlpha(change.getLeash(), 1.f);
-                    } else {
-                        // We are receiving new opening tasks, so convert to onTasksAppeared.
-                        appearedTargets[i] = TransitionUtil.newTarget(
-                                change, layer, info, t, mLeashMap);
-                        // reparent into the original `mInfo` since that's where we are animating.
-                        final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
-                        t.reparent(appearedTargets[i].leash, mInfo.getRoot(rootIdx).getLeash());
-                        t.setLayer(appearedTargets[i].leash, layer);
-                        mOpeningTasks.add(new TaskState(change, appearedTargets[i].leash));
-                    }
-                }
-                didMergeThings = true;
-                mState = STATE_NEW_TASK;
-            }
-            if (!didMergeThings) {
-                // Didn't recognize anything in incoming transition so don't merge it.
-                Slog.w(TAG, "Don't know how to merge this transition.");
-                return;
-            }
-            // At this point, we are accepting the merge.
-            t.apply();
-            // not using the incoming anim-only surfaces
-            info.releaseAnimSurfaces();
-            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
-            if (appearedTargets == null) return;
-            try {
-                mListener.onTasksAppeared(appearedTargets);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
-            }
-        }
-
-        @Override
-        public TaskSnapshot screenshotTask(int taskId) {
-            try {
-                return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to screenshot task", e);
-            }
-            return null;
-        }
-
-        @Override
-        public void setInputConsumerEnabled(boolean enabled) {
-            mExecutor.execute(() -> {
-                if (mFinishCB == null || !enabled) return;
-                // transient launches don't receive focus automatically. Since we are taking over
-                // the gesture now, take focus explicitly.
-                // This also moves recents back to top if the user gestured before a switch
-                // animation finished.
-                try {
-                    ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to set focused task", e);
-                }
-            });
-        }
-
-        @Override
-        public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) {
-        }
-
-        @Override
-        public void setFinishTaskTransaction(int taskId,
-                PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
-            mExecutor.execute(() -> {
-                if (mFinishCB == null) return;
-                mPipTransaction = finishTransaction;
-            });
-        }
-
-        @Override
-        @SuppressLint("NewApi")
-        public void finish(boolean toHome, boolean sendUserLeaveHint) {
-            mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint));
-        }
-
-        private void finishInner(boolean toHome, boolean sendUserLeaveHint) {
-            if (mFinishCB == null) {
-                Slog.e(TAG, "Duplicate call to finish");
-                return;
-            }
-            final Transitions.TransitionFinishCallback finishCB = mFinishCB;
-            mFinishCB = null;
-
-            final SurfaceControl.Transaction t = mFinishTransaction;
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-
-            if (mKeyguardLocked && mRecentsTask != null) {
-                if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
-                else wct.restoreTransientOrder(mRecentsTask);
-            }
-            if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
-                // The gesture is returning to the pausing-task(s) rather than continuing with
-                // recents, so end the transition by moving the app back to the top (and also
-                // re-showing it's task).
-                for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
-                    // reverse order so that index 0 ends up on top
-                    wct.reorder(mPausingTasks.get(i).mToken, true /* onTop */);
-                    t.show(mPausingTasks.get(i).mTaskSurface);
-                }
-                if (!mKeyguardLocked && mRecentsTask != null) {
-                    wct.restoreTransientOrder(mRecentsTask);
-                }
-            } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
-                // Special situation where 3p launcher was changed during recents (this happens
-                // during tapltests...). Here we get both "return to home" AND "home opening".
-                // This is basically going home, but we have to restore the recents and home order.
-                for (int i = 0; i < mOpeningTasks.size(); ++i) {
-                    final TaskState state = mOpeningTasks.get(i);
-                    if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
-                        // Make sure it is on top.
-                        wct.reorder(state.mToken, true /* onTop */);
-                    }
-                    t.show(state.mTaskSurface);
-                }
-                for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
-                    t.hide(mPausingTasks.get(i).mTaskSurface);
-                }
-                if (!mKeyguardLocked && mRecentsTask != null) {
-                    wct.restoreTransientOrder(mRecentsTask);
-                }
-            } else {
-                // The general case: committing to recents, going home, or switching tasks.
-                for (int i = 0; i < mOpeningTasks.size(); ++i) {
-                    t.show(mOpeningTasks.get(i).mTaskSurface);
-                }
-                for (int i = 0; i < mPausingTasks.size(); ++i) {
-                    if (!sendUserLeaveHint) {
-                        // This means recents is not *actually* finishing, so of course we gotta
-                        // do special stuff in WMCore to accommodate.
-                        wct.setDoNotPip(mPausingTasks.get(i).mToken);
-                    }
-                    // Since we will reparent out of the leashes, pre-emptively hide the child
-                    // surface to match the leash. Otherwise, there will be a flicker before the
-                    // visibility gets committed in Core when using split-screen (in splitscreen,
-                    // the leaf-tasks are not "independent" so aren't hidden by normal setup).
-                    t.hide(mPausingTasks.get(i).mTaskSurface);
-                }
-                if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) {
-                    t.show(mInfo.getChange(mPipTask).getLeash());
-                    PictureInPictureSurfaceTransaction.apply(mPipTransaction,
-                            mInfo.getChange(mPipTask).getLeash(), t);
-                    mPipTask = null;
-                    mPipTransaction = null;
-                }
-            }
-            cleanUp();
-            finishCB.onTransitionFinished(wct.isEmpty() ? null : wct, null /* wctCB */);
-        }
-
-        @Override
-        public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
-        }
-
-        @Override
-        public void cleanupScreenshot() {
-        }
-
-        @Override
-        public void setWillFinishToHome(boolean willFinishToHome) {
-            mExecutor.execute(() -> {
-                mWillFinishToHome = willFinishToHome;
-            });
-        }
-
-        /**
-         * @see IRecentsAnimationController#removeTask
-         */
-        @Override
-        public boolean removeTask(int taskId) {
-            return false;
-        }
-
-        /**
-         * @see IRecentsAnimationController#detachNavigationBarFromApp
-         */
-        @Override
-        public void detachNavigationBarFromApp(boolean moveHomeToTop) {
-            mExecutor.execute(() -> {
-                if (mTransition == null) return;
-                try {
-                    ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to detach the navigation bar from app", e);
-                }
-            });
-        }
-
-        /**
-         * @see IRecentsAnimationController#animateNavigationBarToApp(long)
-         */
-        @Override
-        public void animateNavigationBarToApp(long duration) {
-        }
-    };
-
-    /** Utility class to track the state of a task as-seen by recents. */
-    private static class TaskState {
-        WindowContainerToken mToken;
-        ActivityManager.RunningTaskInfo mTaskInfo;
-
-        /** The surface/leash of the task provided by Core. */
-        SurfaceControl mTaskSurface;
-
-        /** The (local) animation-leash created for this task. */
-        SurfaceControl mLeash;
-
-        TaskState(TransitionInfo.Change change, SurfaceControl leash) {
-            mToken = change.getContainer();
-            mTaskInfo = change.getTaskInfo();
-            mTaskSurface = change.getLeash();
-            mLeash = leash;
-        }
-
-        static int indexOf(ArrayList<TaskState> list, TransitionInfo.Change change) {
-            for (int i = list.size() - 1; i >= 0; --i) {
-                if (list.get(i).mToken.equals(change.getContainer())) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        public String toString() {
-            return "" + mToken + " : " + mLeash;
-        }
-    }
-
-    /**
-     * An interface for a mixed handler to receive information about recents requests (since these
-     * come into this handler directly vs from WMCore request).
-     */
-    public interface RecentsMixedHandler {
-        /**
-         * Called when a recents request comes in. The handler can add operations to outWCT. If
-         * the handler wants to "accept" the transition, it should return itself; otherwise, it
-         * should return `null`.
-         *
-         * If a mixed-handler accepts this recents, it will be the de-facto handler for this
-         * transition and is required to call the associated {@link #startAnimation},
-         * {@link #mergeAnimation}, and {@link #onTransitionConsumed} methods.
-         */
-        Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT);
-
-        /**
-         * Reports the transition token associated with the accepted recents request. If there was
-         * a problem starting the request, this will be called with `null`.
-         */
-        void setRecentsTransition(@Nullable IBinder transition);
-    }
-}
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 94b9e90..7d5ab84 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
@@ -31,7 +31,6 @@
 import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
 import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
 import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -89,7 +88,6 @@
 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
@@ -329,9 +327,14 @@
         return mTaskOrganizer.getRunningTaskInfo(taskId);
     }
 
+    /** Check task is under split or not by taskId. */
     public boolean isTaskInSplitScreen(int taskId) {
-        return isSplitScreenVisible()
-                && mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
+        return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
+    }
+
+    /** Check split is foreground and task is under split or not by taskId. */
+    public boolean isTaskInSplitScreenForeground(int taskId) {
+        return isTaskInSplitScreen(taskId) && isSplitScreenVisible();
     }
 
     public @SplitPosition int getSplitPosition(int taskId) {
@@ -339,8 +342,7 @@
     }
 
     public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
-        return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
-                new WindowContainerTransaction());
+        return moveToStage(taskId, sideStagePosition, new WindowContainerTransaction());
     }
 
     /**
@@ -351,13 +353,13 @@
         mStageCoordinator.updateSurfaces(transaction);
     }
 
-    private boolean moveToStage(int taskId, @StageType int stageType,
-            @SplitPosition int stagePosition, WindowContainerTransaction wct) {
+    private boolean moveToStage(int taskId, @SplitPosition int stagePosition,
+            WindowContainerTransaction wct) {
         final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
         if (task == null) {
             throw new IllegalArgumentException("Unknown taskId" + taskId);
         }
-        return mStageCoordinator.moveToStage(task, stageType, stagePosition, wct);
+        return mStageCoordinator.moveToStage(task, stagePosition, wct);
     }
 
     public boolean removeFromSideStage(int taskId) {
@@ -382,10 +384,9 @@
     }
 
     public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
-        final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
         final int stagePosition =
                 leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
-        moveToStage(taskId, stageType, stagePosition, wct);
+        moveToStage(taskId, stagePosition, wct);
     }
 
     public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
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 a1eaf85..def945e 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
@@ -373,56 +373,43 @@
         return STAGE_TYPE_UNDEFINED;
     }
 
-    boolean moveToStage(ActivityManager.RunningTaskInfo task, @StageType int stageType,
-            @SplitPosition int stagePosition, WindowContainerTransaction wct) {
+    boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
+            WindowContainerTransaction wct) {
         StageTaskListener targetStage;
         int sideStagePosition;
-        if (stageType == STAGE_TYPE_MAIN) {
-            targetStage = mMainStage;
-            sideStagePosition = reverseSplitPosition(stagePosition);
-        } else if (stageType == STAGE_TYPE_SIDE) {
+        if (isSplitScreenVisible()) {
+            // If the split screen is foreground, retrieves target stage based on position.
+            targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage;
+            sideStagePosition = mSideStagePosition;
+        } else {
             targetStage = mSideStage;
             sideStagePosition = stagePosition;
-        } else {
-            if (isSplitScreenVisible()) {
-                // If the split screen is activated, retrieves target stage based on position.
-                targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage;
-                sideStagePosition = mSideStagePosition;
-            } else {
-                // Exit split if it running background.
-                exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
-
-                targetStage = mSideStage;
-                sideStagePosition = stagePosition;
-            }
         }
 
         if (!isSplitActive()) {
-            // prevent the fling divider to center transitioni if split screen didn't active.
-            mIsDropEntering = true;
+            mSplitLayout.init();
+            prepareEnterSplitScreen(wct, task, stagePosition);
+            mSyncQueue.queue(wct);
+            mSyncQueue.runInSync(t -> {
+                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+            });
+        } else {
+            setSideStagePosition(sideStagePosition, wct);
+            targetStage.addTask(task, wct);
+            targetStage.evictAllChildren(wct);
+            if (!isSplitScreenVisible()) {
+                final StageTaskListener anotherStage = targetStage == mMainStage
+                        ? mSideStage : mMainStage;
+                anotherStage.reparentTopTask(wct);
+                anotherStage.evictAllChildren(wct);
+                wct.reorder(mRootTaskInfo.token, true);
+            }
+            setRootForceTranslucent(false, wct);
+            mSyncQueue.queue(wct);
         }
 
-        setSideStagePosition(sideStagePosition, wct);
-        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
-        targetStage.evictAllChildren(evictWct);
-
-        // Apply surface bounds before animation start.
-        SurfaceControl.Transaction startT = mTransactionPool.acquire();
-        if (startT != null) {
-            updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
-            startT.apply();
-            mTransactionPool.release(startT);
-        }
-        // reparent the task to an invisible split root will make the activity invisible.  Reorder
-        // the root task to front to make the entering transition from pip to split smooth.
-        wct.reorder(mRootTaskInfo.token, true);
-        wct.reorder(targetStage.mRootTaskInfo.token, true);
-        targetStage.addTask(task, wct);
-
-        if (!evictWct.isEmpty()) {
-            wct.merge(evictWct, true /* transfer */);
-        }
-        mTaskOrganizer.applyTransaction(wct);
+        // Due to drag already pip task entering split by this method so need to reset flag here.
+        mIsDropEntering = false;
         return true;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index aa1e6ed..aa851d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -43,7 +43,6 @@
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.recents.RecentsTransitionHandler;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.splitscreen.StageCoordinator;
 import com.android.wm.shell.sysui.ShellInit;
@@ -56,12 +55,10 @@
  * A handler for dealing with transitions involving multiple other handlers. For example: an
  * activity in split-screen going into PiP.
  */
-public class DefaultMixedHandler implements Transitions.TransitionHandler,
-        RecentsTransitionHandler.RecentsMixedHandler {
+public class DefaultMixedHandler implements Transitions.TransitionHandler {
 
     private final Transitions mPlayer;
     private PipTransitionController mPipHandler;
-    private RecentsTransitionHandler mRecentsHandler;
     private StageCoordinator mSplitHandler;
 
     private static class MixedTransition {
@@ -125,8 +122,7 @@
 
     public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
             Optional<SplitScreenController> splitScreenControllerOptional,
-            Optional<PipTouchHandler> pipTouchHandlerOptional,
-            Optional<RecentsTransitionHandler> recentsHandlerOptional) {
+            Optional<PipTouchHandler> pipTouchHandlerOptional) {
         mPlayer = player;
         if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
                 && splitScreenControllerOptional.isPresent()) {
@@ -138,10 +134,6 @@
                 if (mSplitHandler != null) {
                     mSplitHandler.setMixedHandler(this);
                 }
-                mRecentsHandler = recentsHandlerOptional.orElse(null);
-                if (mRecentsHandler != null) {
-                    mRecentsHandler.addMixer(this);
-                }
             }, this);
         }
     }
@@ -208,29 +200,6 @@
         return null;
     }
 
-    @Override
-    public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
-        if (mRecentsHandler != null && mSplitHandler.isSplitActive()) {
-            return this;
-        }
-        return null;
-    }
-
-    @Override
-    public void setRecentsTransition(IBinder transition) {
-        if (mSplitHandler.isSplitActive()) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
-                    + "Split-Screen is active, so treat it as Mixed.");
-            final MixedTransition mixed = new MixedTransition(
-                    MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
-            mixed.mLeftoversHandler = mRecentsHandler;
-            mActiveTransitions.add(mixed);
-        } else {
-            throw new IllegalStateException("Accepted a recents transition but don't know how to"
-                    + " handle it");
-        }
-    }
-
     private TransitionInfo subCopy(@NonNull TransitionInfo info,
             @WindowManager.TransitionType int newType, boolean withChanges) {
         final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
@@ -303,13 +272,18 @@
                 info.getChanges().remove(i);
             }
         }
+        Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
+            --mixed.mInFlightSubAnimations;
+            mixed.joinFinishArgs(wct, wctCB);
+            if (mixed.mInFlightSubAnimations > 0) return;
+            mActiveTransitions.remove(mixed);
+            finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
+        };
         if (pipChange == null) {
             if (mixed.mLeftoversHandler != null) {
+                mixed.mInFlightSubAnimations = 1;
                 if (mixed.mLeftoversHandler.startAnimation(mixed.mTransition,
-                        info, startTransaction, finishTransaction, (wct, wctCB) -> {
-                            mActiveTransitions.remove(mixed);
-                            finishCallback.onTransitionFinished(wct, wctCB);
-                        })) {
+                        info, startTransaction, finishTransaction, finishCB)) {
                     return true;
                 }
             }
@@ -318,13 +292,6 @@
         }
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate"
                         + " animation because remote-animation likely doesn't support it");
-        Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
-            --mixed.mInFlightSubAnimations;
-            mixed.joinFinishArgs(wct, wctCB);
-            if (mixed.mInFlightSubAnimations > 0) return;
-            mActiveTransitions.remove(mixed);
-            finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
-        };
         // Split the transition into 2 parts: the pip part and the rest.
         mixed.mInFlightSubAnimations = 2;
         // make a new startTransaction because pip's startEnterAnimation "consumes" it so
@@ -594,8 +561,6 @@
             mPipHandler.onTransitionConsumed(transition, aborted, finishT);
         } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
             mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
-        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
-            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
         }
     }
 }
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 039bde9..b8b6d5b 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
@@ -534,7 +534,7 @@
         }
 
         if (info.getType() == TRANSIT_SLEEP) {
-            if (activeIdx > 0) {
+            if (activeIdx > 0 || !mActiveTransitions.isEmpty() || mReadyTransitions.size() > 1) {
                 // Sleep starts a process of forcing all prior transitions to finish immediately
                 finishForSleep(null /* forceFinish */);
                 return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
index 86ca292..fe0a3fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
@@ -79,7 +79,7 @@
     }
 
     private float[] getBackgroundColor(Context context) {
-        int colorInt = context.getResources().getColor(R.color.taskbar_background);
+        int colorInt = context.getResources().getColor(R.color.unfold_background);
         return new float[]{
                 (float) red(colorInt) / 255.0F,
                 (float) green(colorInt) / 255.0F,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index ffdb87f..5b06c9c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -79,7 +79,8 @@
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
-    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false,
+            appExistAtStart = false)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 792e2b0..c840183 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -82,7 +82,8 @@
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
-    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, sendNotificationApp,
+            fromOtherApp = false)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 8b025cd..919bf06 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -185,7 +185,8 @@
 
         Intent appBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
         appBubbleIntent.setPackage(mContext.getPackageName());
-        mAppBubble = new Bubble(appBubbleIntent, new UserHandle(1), mMainExecutor);
+        mAppBubble = new Bubble(appBubbleIntent, new UserHandle(1), mock(Icon.class),
+                mMainExecutor);
 
         mPositioner = new TestableBubblePositioner(mContext,
                 mock(WindowManager.class));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 2e2e49e..eda6fdc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -145,39 +145,48 @@
     }
 
     @Test
-    public void testMoveToStage() {
+    public void testMoveToStage_splitActiveBackground() {
+        when(mStageCoordinator.isSplitActive()).thenReturn(true);
+
         final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
 
-        mStageCoordinator.moveToStage(task, STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT,
-                new WindowContainerTransaction());
-        verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
-        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
-
-        mStageCoordinator.moveToStage(task, STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT,
-                new WindowContainerTransaction());
-        verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
+        mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+        verify(mSideStage).addTask(eq(task), eq(wct));
         assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
+        assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
     }
 
     @Test
-    public void testMoveToUndefinedStage() {
-        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
-
-        // Verify move to undefined stage while split screen not activated moves task to side stage.
-        when(mStageCoordinator.isSplitScreenVisible()).thenReturn(false);
-        mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
-        mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
-                new WindowContainerTransaction());
-        verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
-        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
-
-        // Verify move to undefined stage after split screen activated moves task based on position.
+    public void testMoveToStage_splitActiveForeground() {
+        when(mStageCoordinator.isSplitActive()).thenReturn(true);
         when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true);
-        assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
-        mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
-                new WindowContainerTransaction());
-        verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
-        assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
+        // Assume current side stage is top or left.
+        mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
+
+        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+        mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+        verify(mMainStage).addTask(eq(task), eq(wct));
+        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
+        assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition());
+
+        mStageCoordinator.moveToStage(task, SPLIT_POSITION_TOP_OR_LEFT, wct);
+        verify(mSideStage).addTask(eq(task), eq(wct));
+        assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition());
+        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
+    }
+
+    @Test
+    public void testMoveToStage_splitInctive() {
+        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+        mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+        verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
+                eq(SPLIT_POSITION_BOTTOM_OR_RIGHT));
+        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
     }
 
     @Test
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 8ab7159..8efd180 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6251,6 +6251,7 @@
      * Volume behavior for an audio device where no software attenuation is applied, and
      *     the volume is kept synchronized between the host and the device itself through a
      *     device-specific protocol such as BT AVRCP.
+     * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
      */
     @SystemApi
     public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
@@ -6261,6 +6262,7 @@
      *     device-specific protocol (such as for hearing aids), based on the audio mode (e.g.
      *     normal vs in phone call).
      * @see #setMode(int)
+     * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
      */
     @SystemApi
     public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
@@ -6270,11 +6272,6 @@
      * A variant of {@link #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} where the host cannot reliably set
      * the volume percentage of the audio device. Specifically, {@link #setStreamVolume} will have
      * no effect, or an unreliable effect.
-     *
-     * {@link #DEVICE_VOLUME_BEHAVIOR_FULL} will be returned instead by
-     * {@link #getDeviceVolumeBehavior} for target SDK versions before U.
-     *
-     * @see #RETURN_DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY
      */
     @SystemApi
     public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5;
@@ -6316,27 +6313,18 @@
     public @interface AbsoluteDeviceVolumeBehavior {}
 
     /**
-     * Volume behaviors that can be set with {@link #setDeviceVolumeBehavior}.
      * @hide
-     */
-    @IntDef({
-        DEVICE_VOLUME_BEHAVIOR_VARIABLE,
-        DEVICE_VOLUME_BEHAVIOR_FULL,
-        DEVICE_VOLUME_BEHAVIOR_FIXED,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SettableDeviceVolumeBehavior {}
-
-    /**
-     * @hide
-     * Throws IAE on a non-settable volume behavior value
+     * Throws IAE on an invalid volume behavior value
      * @param volumeBehavior behavior value to check
      */
-    public static void enforceSettableVolumeBehavior(int volumeBehavior) {
+    public static void enforceValidVolumeBehavior(int volumeBehavior) {
         switch (volumeBehavior) {
             case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
             case DEVICE_VOLUME_BEHAVIOR_FULL:
             case DEVICE_VOLUME_BEHAVIOR_FIXED:
+            case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+            case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+            case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
                 return;
             default:
                 throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
@@ -6346,8 +6334,11 @@
     /**
      * @hide
      * Sets the volume behavior for an audio output device.
-     *
-     * @see SettableDeviceVolumeBehavior
+     * @see #DEVICE_VOLUME_BEHAVIOR_VARIABLE
+     * @see #DEVICE_VOLUME_BEHAVIOR_FULL
+     * @see #DEVICE_VOLUME_BEHAVIOR_FIXED
+     * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
+     * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
      * @param device the device to be affected
      * @param deviceVolumeBehavior one of the device behaviors
      */
@@ -6357,10 +6348,10 @@
             Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
     })
     public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
-            @SettableDeviceVolumeBehavior int deviceVolumeBehavior) {
+            @DeviceVolumeBehavior int deviceVolumeBehavior) {
         // verify arguments (validity of device type is enforced in server)
         Objects.requireNonNull(device);
-        enforceSettableVolumeBehavior(deviceVolumeBehavior);
+        enforceValidVolumeBehavior(deviceVolumeBehavior);
         // communicate with service
         final IAudioService service = getService();
         try {
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 9b238e1..6744359 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -49,6 +49,7 @@
 import libcore.io.IoUtils;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -255,17 +256,19 @@
 
         // get orientation
         if (MediaFile.isExifMimeType(mimeType)) {
-            exif = new ExifInterface(file);
-            switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) {
-                case ExifInterface.ORIENTATION_ROTATE_90:
-                    orientation = 90;
-                    break;
-                case ExifInterface.ORIENTATION_ROTATE_180:
-                    orientation = 180;
-                    break;
-                case ExifInterface.ORIENTATION_ROTATE_270:
-                    orientation = 270;
-                    break;
+            try (FileInputStream is = new FileInputStream(file)) {
+                exif = new ExifInterface(is.getFD());
+                switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) {
+                    case ExifInterface.ORIENTATION_ROTATE_90:
+                        orientation = 90;
+                        break;
+                    case ExifInterface.ORIENTATION_ROTATE_180:
+                        orientation = 180;
+                        break;
+                    case ExifInterface.ORIENTATION_ROTATE_270:
+                        orientation = 270;
+                        break;
+                }
             }
         }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index b32fe3f..28f9453 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -467,19 +467,19 @@
             GetCredentialRequest.Builder(
                 Bundle()
             ).addCredentialOption(
-                CredentialOption(
+                CredentialOption.Builder(
                     passwordOption.type,
                     passwordOption.requestData,
                     passwordOption.candidateQueryData,
-                    passwordOption.isSystemProviderRequired
-                )
+                ).setIsSystemProviderRequired(passwordOption.isSystemProviderRequired)
+                .build()
             ).addCredentialOption(
-                CredentialOption(
+                CredentialOption.Builder(
                     passkeyOption.type,
                     passkeyOption.requestData,
                     passkeyOption.candidateQueryData,
-                    passkeyOption.isSystemProviderRequired
-                )
+                ).setIsSystemProviderRequired(passkeyOption.isSystemProviderRequired)
+                .build()
             ).build(),
             "com.google.android.youtube"
         )
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 6669e35..a2118fa 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -37,9 +37,8 @@
     <string name="install_confirm_question">Do you want to install this app?</string>
     <!-- Message for updating an existing app [CHAR LIMIT=NONE] -->
     <string name="install_confirm_question_update">Do you want to update this app?</string>
-    <!-- TODO(b/244413073) Revise the description after getting UX input and UXR on this. -->
-    <!-- Message for updating an existing app with update owner reminder [DO NOT TRANSLATE][CHAR LIMIT=NONE] -->
-    <string name="install_confirm_question_update_owner_reminder">Updates to this app are currently managed by <xliff:g id="existing_update_owner">%1$s</xliff:g>.\n\nDo you want to install this update from <xliff:g id="new_update_owner">%2$s</xliff:g>?</string>
+    <!-- Message for updating an existing app with update owner reminder [CHAR LIMIT=NONE] -->
+    <string name="install_confirm_question_update_owner_reminder">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your phone. App functionality may change.</string>
     <!-- [CHAR LIMIT=100] -->
     <string name="install_failed">App not installed.</string>
     <!-- Reason displayed when installation fails because the package was blocked
@@ -82,6 +81,8 @@
 
     <!-- [CHAR LIMIT=15] -->
     <string name="ok">OK</string>
+    <!-- [CHAR LIMIT=30] -->
+    <string name="update_anyway">Update anyway</string>
     <!-- [CHAR LIMIT=15] -->
     <string name="manage_applications">Manage apps</string>
     <!-- [CHAR LIMIT=30] -->
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index d41cfbc2..c81e75b 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -148,10 +148,11 @@
                     && mPendingUserActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP) {
                 viewToEnable.setText(
                         getString(R.string.install_confirm_question_update_owner_reminder,
-                                existingUpdateOwnerLabel, requestedUpdateOwnerLabel));
+                                requestedUpdateOwnerLabel, existingUpdateOwnerLabel));
+                mOk.setText(R.string.update_anyway);
+            } else {
+                mOk.setText(R.string.update);
             }
-
-            mOk.setText(R.string.update);
         } else {
             // This is a new application with no permissions.
             viewToEnable = requireViewById(R.id.install_confirm_question);
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index f6bb3cc..47ac2df 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -109,7 +109,7 @@
     scrollBehavior: TopAppBarScrollBehavior? = null,
 ) {
     TwoRowsTopAppBar(
-        title = { Title(title = title, maxLines = 2) },
+        title = { Title(title = title, maxLines = 3) },
         titleTextStyle = MaterialTheme.typography.displaySmall,
         smallTitleTextStyle = MaterialTheme.typography.titleMedium,
         titleBottomPadding = LargeTitleBottomPadding,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
index c609004..9f33fcb 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
@@ -62,10 +62,12 @@
         }
 
         val permission = AppOpsManager.opToPermission(op)
-        packageManager.updatePermissionFlags(permission, app.packageName,
-                PackageManager.FLAG_PERMISSION_USER_SET, PackageManager.FLAG_PERMISSION_USER_SET,
-                UserHandle.getUserHandleForUid(app.uid))
-
+        if (permission != null) {
+            packageManager.updatePermissionFlags(permission, app.packageName,
+                    PackageManager.FLAG_PERMISSION_USER_SET,
+                    PackageManager.FLAG_PERMISSION_USER_SET,
+                    UserHandle.getUserHandleForUid(app.uid))
+        }
         _mode.postValue(mode)
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index a8eeec3..80030f7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -50,10 +50,6 @@
     @GuardedBy("mLock")
     private final ArrayMap<Integer, ArrayMap<String, Integer>> mKeyToIndexMapMap = new ArrayMap<>();
 
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    // Maximum number of backing stores allowed
-    static final int NUM_MAX_BACKING_STORE = 8;
-
     @GuardedBy("mLock")
     private int mNumBackingStore = 0;
 
@@ -65,8 +61,24 @@
     // The generation number is only increased when a new non-predefined setting is inserted
     private static final String DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS = "";
 
-    public GenerationRegistry(Object lock) {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    // Minimum number of backing stores; supports 3 users
+    static final int MIN_NUM_BACKING_STORE = 8;
+    // Maximum number of backing stores; supports 18 users
+    static final int MAX_NUM_BACKING_STORE = 38;
+
+    private final int mMaxNumBackingStore;
+
+    GenerationRegistry(Object lock, int maxNumUsers) {
         mLock = lock;
+        // Add some buffer to maxNumUsers to accommodate corner cases when the actual number of
+        // users in the system exceeds the limit
+        maxNumUsers = maxNumUsers + 2;
+        // Number of backing stores needed for N users is (N + N + 1 + 1) = N * 2 + 2
+        // N Secure backing stores and N System backing stores, 1 Config and 1 Global for all users
+        // However, we always make sure that at least 3 users and at most 18 users are supported.
+        mMaxNumBackingStore = Math.min(Math.max(maxNumUsers * 2 + 2, MIN_NUM_BACKING_STORE),
+                MAX_NUM_BACKING_STORE);
     }
 
     /**
@@ -195,7 +207,7 @@
         }
         if (backingStore == null) {
             try {
-                if (mNumBackingStore >= NUM_MAX_BACKING_STORE) {
+                if (mNumBackingStore >= mMaxNumBackingStore) {
                     if (DEBUG) {
                         Slog.e(LOG_TAG, "Error creating backing store - at capacity");
                     }
@@ -275,4 +287,9 @@
         }
         return -1;
     }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    int getMaxNumBackingStores() {
+        return mMaxNumBackingStore;
+    }
 }
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7e89bfc..721b3c4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2831,7 +2831,7 @@
 
         public SettingsRegistry() {
             mHandler = new MyHandler(getContext().getMainLooper());
-            mGenerationRegistry = new GenerationRegistry(mLock);
+            mGenerationRegistry = new GenerationRegistry(mLock, UserManager.getMaxSupportedUsers());
             mBackupManager = new BackupManager(getContext());
         }
 
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 47abb35..e0e3720 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -265,7 +265,6 @@
                     Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
                     Settings.Global.ENABLE_DISKSTATS_LOGGING,
                     Settings.Global.ENABLE_EPHEMERAL_FEATURE,
-                    Settings.Global.ENABLE_RESTRICTED_BUCKET,
                     Settings.Global.ENABLE_TARE,
                     Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
                     Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
index 586d6f7..12865f4 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
@@ -36,7 +36,7 @@
 public class GenerationRegistryTest {
     @Test
     public void testGenerationsWithRegularSetting() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
         final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
         final String testSecureSetting = "test_secure_setting";
         Bundle b = new Bundle();
@@ -93,7 +93,7 @@
 
     @Test
     public void testGenerationsWithConfigSetting() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
         final String prefix = "test_namespace/";
         final int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
 
@@ -110,10 +110,10 @@
 
     @Test
     public void testMaxNumBackingStores() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
         final String testSecureSetting = "test_secure_setting";
         Bundle b = new Bundle();
-        for (int i = 0; i < GenerationRegistry.NUM_MAX_BACKING_STORE; i++) {
+        for (int i = 0; i < generationRegistry.getMaxNumBackingStores(); i++) {
             b.clear();
             final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, i);
             generationRegistry.addGenerationData(b, key, testSecureSetting);
@@ -121,7 +121,7 @@
         }
         b.clear();
         final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE,
-                GenerationRegistry.NUM_MAX_BACKING_STORE + 1);
+                generationRegistry.getMaxNumBackingStores() + 1);
         generationRegistry.addGenerationData(b, key, testSecureSetting);
         // Should fail to add generation because the number of backing stores has reached limit
         checkBundle(b, -1, -1, true);
@@ -133,7 +133,7 @@
 
     @Test
     public void testMaxSizeBackingStore() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
         final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
         final String testSecureSetting = "test_secure_setting";
         Bundle b = new Bundle();
@@ -153,7 +153,7 @@
 
     @Test
     public void testUnsetSettings() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
         final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
         final String testSecureSetting = "test_secure_setting";
         Bundle b = new Bundle();
@@ -172,7 +172,7 @@
 
     @Test
     public void testGlobalSettings() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
         final int globalKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, 0);
         final String testGlobalSetting = "test_global_setting";
         final Bundle b = new Bundle();
@@ -188,6 +188,18 @@
         assertThat(array).isEqualTo(array2);
     }
 
+    @Test
+    public void testNumberOfBackingStores() {
+        GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 0);
+        // Test that the capacity of the backing stores is always valid
+        assertThat(generationRegistry.getMaxNumBackingStores()).isEqualTo(
+                GenerationRegistry.MIN_NUM_BACKING_STORE);
+        generationRegistry = new GenerationRegistry(new Object(), 100);
+        // Test that the capacity of the backing stores is always valid
+        assertThat(generationRegistry.getMaxNumBackingStores()).isEqualTo(
+                GenerationRegistry.MAX_NUM_BACKING_STORE);
+    }
+
     private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull)
             throws IOException {
         final MemoryIntArray array = getArray(b);
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt
index b9e38cf..99fe26c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt
@@ -53,6 +53,7 @@
     modifier: Modifier = Modifier,
 ) {
     val isScrimEnabled: Boolean by viewModel.isScrimEnabled.collectAsState()
+    val scrimAlpha: Float by viewModel.scrimAlpha.collectAsState()
 
     // TODO(b/273298030): find a different way to get the height constraint from its parent.
     BoxWithConstraints(modifier = modifier) {
@@ -61,7 +62,7 @@
         Scrim(
             modifier = Modifier.fillMaxSize(),
             remoteTouch = viewModel::onScrimTouched,
-            alpha = { viewModel.scrimAlpha.value },
+            alpha = { scrimAlpha },
             isScrimEnabled = isScrimEnabled,
         )
         Shade(
diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml
index a317178..762dcdc 100644
--- a/packages/SystemUI/res/layout/chipbar.xml
+++ b/packages/SystemUI/res/layout/chipbar.xml
@@ -55,7 +55,7 @@
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:textSize="@dimen/chipbar_text_size"
-            android:textColor="@android:color/system_accent2_900"
+            android:textColor="@color/chipbar_text_and_icon_color"
             android:alpha="0.0"
             />
 
diff --git a/packages/SystemUI/res/layout/status_bar_user_chip_container.xml b/packages/SystemUI/res/layout/status_bar_user_chip_container.xml
index b374074..80f5d87 100644
--- a/packages/SystemUI/res/layout/status_bar_user_chip_container.xml
+++ b/packages/SystemUI/res/layout/status_bar_user_chip_container.xml
@@ -24,7 +24,7 @@
     android:orientation="horizontal"
     android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
     android:background="@drawable/status_bar_user_chip_bg"
-    android:visibility="visible" >
+    android:visibility="gone" >
     <ImageView android:id="@+id/current_user_avatar"
         android:layout_width="@dimen/status_bar_user_chip_avatar_size"
         android:layout_height="@dimen/status_bar_user_chip_avatar_size"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 62e8c5f..31071ca 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -231,6 +231,9 @@
 
     <color name="people_tile_background">@color/material_dynamic_secondary95</color>
 
+    <!-- Chipbar -->
+    <color name="chipbar_text_and_icon_color">@android:color/system_accent2_900</color>
+
     <!-- Internet Dialog -->
     <!-- Material next state on color-->
     <color name="settingslib_state_on_color">@color/settingslib_state_on</color>
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 4aaa566..3b9060a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -259,7 +259,7 @@
                 largeTimeListener?.update(shouldTimeListenerRun)
             }
 
-            override fun onTimeFormatChanged(timeFormat: String) {
+            override fun onTimeFormatChanged(timeFormat: String?) {
                 clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
             }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 5ec59ab..7b781ce 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -22,6 +22,7 @@
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 
+import android.annotation.Nullable;
 import android.database.ContentObserver;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -458,6 +459,7 @@
         mView.setClock(clock, mStatusBarStateController.getState());
     }
 
+    @Nullable
     private ClockController getClock() {
         return mClockEventController.getClock();
     }
@@ -510,7 +512,9 @@
     }
 
     /** Gets the animations for the current clock. */
+    @Nullable
     public ClockAnimations getClockAnimations() {
-        return getClock().getAnimations();
+        ClockController clock = getClock();
+        return clock == null ? null : clock.getAnimations();
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 7255383..1a572b7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -260,6 +260,14 @@
          */
         @Override
         public void finish(boolean strongAuth, int targetUserId) {
+            if (mFeatureFlags.isEnabled(Flags.PREVENT_BYPASS_KEYGUARD)
+                    && !mKeyguardStateController.canDismissLockScreen() && !strongAuth) {
+                Log.e(TAG,
+                        "Tried to dismiss keyguard when lockscreen is not dismissible and user "
+                                + "was not authenticated with a primary security method "
+                                + "(pin/password/pattern).");
+                return;
+            }
             // If there's a pending runnable because the user interacted with a widget
             // and we're leaving keyguard, then run it.
             boolean deferKeyguardDone = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0e2f8f0..f2f0c59 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -69,6 +69,7 @@
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
 
 import android.annotation.AnyThread;
@@ -1610,7 +1611,7 @@
             requestActiveUnlock(
                     ActiveUnlockConfig.ActiveUnlockRequestOrigin.ASSISTANT,
                     "assistant",
-                    false);
+                    /* dismissKeyguard */ true);
         }
     }
 
@@ -1881,6 +1882,11 @@
                         updateFaceListeningState(BIOMETRIC_ACTION_STOP,
                                 FACE_AUTH_UPDATED_POSTURE_CHANGED);
                     }
+                    if (mPostureState == DEVICE_POSTURE_OPENED) {
+                        mLogger.d("Posture changed to open - attempting to request active unlock");
+                        requestActiveUnlockFromWakeReason(PowerManager.WAKE_REASON_UNFOLD_DEVICE,
+                                false);
+                    }
                 }
             };
 
@@ -2007,26 +2013,10 @@
             FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
             updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
                     FACE_AUTH_UPDATED_STARTED_WAKING_UP);
-
-            final ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin =
-                    mActiveUnlockConfig.isWakeupConsideredUnlockIntent(pmWakeReason)
-                            ? ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
-                            : ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE;
-            final String reason = "wakingUp - " + PowerManager.wakeReasonToString(pmWakeReason);
-            if (mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(pmWakeReason)) {
-                requestActiveUnlockDismissKeyguard(
-                        requestOrigin,
-                        reason
-                );
-            } else {
-                requestActiveUnlock(
-                        requestOrigin,
-                        reason
-                );
-            }
         } else {
             mLogger.logSkipUpdateFaceListeningOnWakeup(pmWakeReason);
         }
+        requestActiveUnlockFromWakeReason(pmWakeReason, true);
 
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2660,6 +2650,32 @@
         }
     }
 
+    private void requestActiveUnlockFromWakeReason(@PowerManager.WakeReason int wakeReason,
+            boolean powerManagerWakeup) {
+        if (!mFaceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(wakeReason)) {
+            mLogger.logActiveUnlockRequestSkippedForWakeReasonDueToFaceConfig(wakeReason);
+            return;
+        }
+
+        final ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin =
+                mActiveUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason)
+                        ? ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+                        : ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE;
+        final String reason = "wakingUp - " + PowerManager.wakeReasonToString(wakeReason)
+                + " powerManagerWakeup=" + powerManagerWakeup;
+        if (mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason)) {
+            requestActiveUnlockDismissKeyguard(
+                    requestOrigin,
+                    reason
+            );
+        } else {
+            requestActiveUnlock(
+                    requestOrigin,
+                    reason
+            );
+        }
+    }
+
     /**
      * Attempts to trigger active unlock from trust agent.
      */
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 5162807..1661806 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -62,6 +62,16 @@
         )
     }
 
+    fun logActiveUnlockRequestSkippedForWakeReasonDueToFaceConfig(wakeReason: Int) {
+        logBuffer.log(
+            "ActiveUnlock",
+            DEBUG,
+            { int1 = wakeReason },
+            { "Skip requesting active unlock from wake reason that doesn't trigger face auth" +
+                    " reason=${PowerManager.wakeReasonToString(int1)}" }
+        )
+    }
+
     fun logAuthInterruptDetected(active: Boolean) {
         logBuffer.log(TAG, DEBUG, { bool1 = active }, { "onAuthInterruptDetected($bool1)" })
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 61d039b..c98a62f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -195,7 +195,7 @@
             scope.launch {
                 alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
                     if (isVisible) {
-                        show(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
+                        show(SideFpsUiRequestSource.ALTERNATE_BOUNCER, REASON_AUTH_KEYGUARD)
                     } else {
                         hide(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
                     }
@@ -436,13 +436,17 @@
     @BiometricOverlayConstants.ShowReason reason: Int
 ) {
     fun update() {
-        val c = context.getColor(R.color.biometric_dialog_accent)
-        val chevronFill = context.getColor(R.color.sfps_chevron_fill)
         val isKeyguard = reason == REASON_AUTH_KEYGUARD
         if (isKeyguard) {
+            val color = context.getColor(R.color.numpad_key_color_secondary) // match bouncer color
+            val chevronFill =
+                com.android.settingslib.Utils.getColorAttrDefaultColor(
+                    context,
+                    android.R.attr.textColorPrimaryInverse
+                )
             for (key in listOf(".blue600", ".blue400")) {
                 addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
-                    PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
+                    PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
                 }
             }
             addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
index 5dabbbb..6a6c3eb 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
@@ -16,10 +16,10 @@
 
 package com.android.systemui.common.shared.model
 
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
 
 /** Models an icon with a specific tint. */
 data class TintedIcon(
     val icon: Icon,
-    @AttrRes val tintAttr: Int?,
+    @ColorRes val tint: Int?,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
index dea8cfd..bcc5932 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
@@ -17,15 +17,14 @@
 package com.android.systemui.common.ui.binder
 
 import android.widget.ImageView
-import com.android.settingslib.Utils
 import com.android.systemui.common.shared.model.TintedIcon
 
 object TintedIconViewBinder {
     /**
      * Binds the given tinted icon to the view.
      *
-     * [TintedIcon.tintAttr] will always be applied, meaning that if it is null, then the tint
-     * *will* be reset to null.
+     * [TintedIcon.tint] will always be applied, meaning that if it is null, then the tint *will* be
+     * reset to null.
      */
     fun bind(
         tintedIcon: TintedIcon,
@@ -33,8 +32,8 @@
     ) {
         IconViewBinder.bind(tintedIcon.icon, view)
         view.imageTintList =
-            if (tintedIcon.tintAttr != null) {
-                Utils.getColorAttr(view.context, tintedIcon.tintAttr)
+            if (tintedIcon.tint != null) {
+                view.resources.getColorStateList(tintedIcon.tint, view.context.theme)
             } else {
                 null
             }
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 ee12db8..868e527 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -608,6 +608,7 @@
         if (items.size == 1) {
             spinner.setBackground(null)
             anchor.setOnClickListener(null)
+            anchor.isClickable = false
             return
         } else {
             spinner.background = parent.context.resources
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
index 055cd52..7f567aa 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
@@ -23,6 +23,7 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dreams.callbacks.DreamStatusBarStateCallback;
 import com.android.systemui.dreams.conditions.DreamCondition;
+import com.android.systemui.flags.RestartDozeListener;
 import com.android.systemui.shared.condition.Monitor;
 import com.android.systemui.util.condition.ConditionalCoreStartable;
 
@@ -39,17 +40,19 @@
     private final Monitor mConditionMonitor;
     private final DreamCondition mDreamCondition;
     private final DreamStatusBarStateCallback mCallback;
+    private RestartDozeListener mRestartDozeListener;
 
 
     @Inject
     public DreamMonitor(Monitor monitor, DreamCondition dreamCondition,
             @Named(DREAM_PRETEXT_MONITOR) Monitor pretextMonitor,
-            DreamStatusBarStateCallback callback) {
+            DreamStatusBarStateCallback callback,
+            RestartDozeListener restartDozeListener) {
         super(pretextMonitor);
         mConditionMonitor = monitor;
         mDreamCondition = dreamCondition;
         mCallback = callback;
-
+        mRestartDozeListener = restartDozeListener;
     }
 
     @Override
@@ -61,5 +64,8 @@
         mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
                 .addCondition(mDreamCondition)
                 .build());
+
+        mRestartDozeListener.init();
+        mRestartDozeListener.maybeRestartSleep();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
index 2befce7..5bbfbda 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.dreams.conditions;
 
+import android.app.DreamManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +31,7 @@
  */
 public class DreamCondition extends Condition {
     private final Context mContext;
+    private final DreamManager mDreamManager;
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -39,8 +41,10 @@
     };
 
     @Inject
-    public DreamCondition(Context context) {
+    public DreamCondition(Context context,
+            DreamManager dreamManager) {
         mContext = context;
+        mDreamManager = dreamManager;
     }
 
     private void processIntent(Intent intent) {
@@ -62,8 +66,8 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_DREAMING_STARTED);
         filter.addAction(Intent.ACTION_DREAMING_STOPPED);
-        final Intent stickyIntent = mContext.registerReceiver(mReceiver, filter);
-        processIntent(stickyIntent);
+        mContext.registerReceiver(mReceiver, filter);
+        updateCondition(mDreamManager.isDreaming());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
index 06ca0ad..28c45b8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.util.InitializationChecker
-import com.android.systemui.util.concurrency.DelayableExecutor
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -38,8 +37,6 @@
     private val featureFlags: FeatureFlagsDebug,
     private val broadcastSender: BroadcastSender,
     private val initializationChecker: InitializationChecker,
-    private val restartDozeListener: RestartDozeListener,
-    private val delayableExecutor: DelayableExecutor
 ) : CoreStartable {
 
     init {
@@ -55,9 +52,6 @@
             // protected broadcast should only be sent for the main process
             val intent = Intent(FlagManager.ACTION_SYSUI_STARTED)
             broadcastSender.sendBroadcast(intent)
-
-            restartDozeListener.init()
-            delayableExecutor.executeDelayed({ restartDozeListener.maybeRestartSleep() }, 1000)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
index 133e67f..f97112d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
@@ -18,8 +18,6 @@
 
 import com.android.systemui.CoreStartable
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.util.InitializationChecker
-import com.android.systemui.util.concurrency.DelayableExecutor
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -31,9 +29,6 @@
 constructor(
     dumpManager: DumpManager,
     featureFlags: FeatureFlags,
-    private val initializationChecker: InitializationChecker,
-    private val restartDozeListener: RestartDozeListener,
-    private val delayableExecutor: DelayableExecutor
 ) : CoreStartable {
 
     init {
@@ -42,12 +37,7 @@
         }
     }
 
-    override fun start() {
-        if (initializationChecker.initializeComponents()) {
-            restartDozeListener.init()
-            delayableExecutor.executeDelayed({ restartDozeListener.maybeRestartSleep() }, 1000)
-        }
-    }
+    override fun start() {}
 }
 
 @Module
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index d05bb8a..a9dd260 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -228,6 +228,11 @@
     @JvmField
     val ASYNC_INFLATE_BOUNCER = unreleasedFlag(229, "async_inflate_bouncer", teamfood = true)
 
+    /** Whether to inflate the bouncer view on a background thread. */
+    // TODO(b/273341787): Tracking Bug
+    @JvmField
+    val PREVENT_BYPASS_KEYGUARD = unreleasedFlag(230, "prevent_bypass_keyguard")
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt b/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt
index bd74f4e..b49d60d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt
@@ -20,7 +20,9 @@
 import android.util.Log
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
@@ -33,6 +35,7 @@
     private val statusBarStateController: StatusBarStateController,
     private val powerManager: PowerManager,
     private val systemClock: SystemClock,
+    @Background val bgExecutor: DelayableExecutor,
 ) {
 
     companion object {
@@ -44,7 +47,7 @@
     val listener =
         object : StatusBarStateController.StateListener {
             override fun onDreamingChanged(isDreaming: Boolean) {
-                settings.putBool(RESTART_NAP_KEY, isDreaming)
+                storeSleepState(isDreaming)
             }
         }
 
@@ -62,11 +65,19 @@
     }
 
     fun maybeRestartSleep() {
-        if (settings.getBool(RESTART_NAP_KEY, false)) {
-            Log.d("RestartDozeListener", "Restarting sleep state")
-            powerManager.wakeUp(systemClock.uptimeMillis())
-            powerManager.goToSleep(systemClock.uptimeMillis())
-            settings.putBool(RESTART_NAP_KEY, false)
-        }
+        bgExecutor.executeDelayed(
+            {
+                if (settings.getBool(RESTART_NAP_KEY, false)) {
+                    Log.d("RestartDozeListener", "Restarting sleep state")
+                    powerManager.wakeUp(systemClock.uptimeMillis())
+                    powerManager.goToSleep(systemClock.uptimeMillis())
+                }
+            },
+            1000
+        )
+    }
+
+    private fun storeSleepState(sleeping: Boolean) {
+        bgExecutor.execute { settings.putBool(RESTART_NAP_KEY, sleeping) }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
index d745a19..aad4a2d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.time.SystemClock
@@ -34,6 +35,7 @@
 class AlternateBouncerInteractor
 @Inject
 constructor(
+    private val statusBarStateController: StatusBarStateController,
     private val keyguardStateController: KeyguardStateController,
     private val bouncerRepository: KeyguardBouncerRepository,
     private val biometricSettingsRepository: BiometricSettingsRepository,
@@ -49,6 +51,17 @@
     var receivedDownTouch = false
     val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
 
+    private val keyguardStateControllerCallback: KeyguardStateController.Callback =
+        object : KeyguardStateController.Callback {
+            override fun onUnlockedChanged() {
+                maybeHide()
+            }
+        }
+
+    init {
+        keyguardStateController.addCallback(keyguardStateControllerCallback)
+    }
+
     /**
      * Sets the correct bouncer states to show the alternate bouncer if it can show.
      *
@@ -109,7 +122,8 @@
                 biometricSettingsRepository.isStrongBiometricAllowed.value &&
                 biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
                 !deviceEntryFingerprintAuthRepository.isLockedOut.value &&
-                !keyguardStateController.isUnlocked
+                !keyguardStateController.isUnlocked &&
+                !statusBarStateController.isDozing
         } else {
             legacyAlternateBouncer != null &&
                 keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true)
@@ -129,6 +143,12 @@
         }
     }
 
+    private fun maybeHide() {
+        if (isVisibleState() && !canShowAlternateBouncerForFingerprint()) {
+            hide()
+        }
+    }
+
     companion object {
         private const val MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS = 200L
         private const val NOT_VISIBLE = -1L
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 889adc7..5704f88 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -160,6 +160,14 @@
         return factory.create("QSLog", 700 /* maxSize */, false /* systrace */);
     }
 
+    /** Provides a logging buffer for logs related to Quick Settings configuration. */
+    @Provides
+    @SysUISingleton
+    @QSConfigLog
+    public static LogBuffer provideQSConfigLogBuffer(LogBufferFactory factory) {
+        return factory.create("QSConfigLog", 100 /* maxSize */, true /* systrace */);
+    }
+
     /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java
new file mode 100644
index 0000000..295bf88
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.plugins.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for QS configuration changed messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface QSConfigLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index ee93c37..dbc2a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -19,12 +19,13 @@
 import android.content.Context
 import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
 import com.android.systemui.R
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo.Companion.DEFAULT_ICON_TINT
 
 /** Utility methods for media tap-to-transfer. */
 class MediaTttUtils {
@@ -78,7 +79,7 @@
                     return IconInfo(
                         contentDescription,
                         MediaTttIcon.Loaded(packageManager.getApplicationIcon(appPackageName)),
-                        tintAttr = null,
+                        tint = null,
                         isAppIcon = true
                     )
                 } catch (e: PackageManager.NameNotFoundException) {
@@ -96,7 +97,7 @@
                     )
                 },
                 MediaTttIcon.Resource(R.drawable.ic_cast),
-                tintAttr = android.R.attr.textColorPrimary,
+                tint = DEFAULT_ICON_TINT,
                 isAppIcon = false
             )
         }
@@ -107,7 +108,7 @@
 data class IconInfo(
     val contentDescription: ContentDescription,
     val icon: MediaTttIcon,
-    @AttrRes val tintAttr: Int?,
+    @ColorRes val tint: Int?,
     /**
      * True if [drawable] is the app's icon, and false if [drawable] is some generic default icon.
      */
@@ -120,7 +121,7 @@
                 is MediaTttIcon.Loaded -> Icon.Loaded(icon.drawable, contentDescription)
                 is MediaTttIcon.Resource -> Icon.Resource(icon.res, contentDescription)
             }
-        return TintedIcon(iconOutput, tintAttr)
+        return TintedIcon(iconOutput, tint)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt
index b9f6d83..ebb8639 100644
--- a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.multishade.data.model.MultiShadeInteractionModel
 import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
 import com.android.systemui.multishade.data.repository.MultiShadeRepository
+import com.android.systemui.multishade.shared.math.isZero
 import com.android.systemui.multishade.shared.model.ProxiedInputModel
 import com.android.systemui.multishade.shared.model.ShadeConfig
 import com.android.systemui.multishade.shared.model.ShadeId
@@ -63,6 +64,10 @@
             }
         }
 
+    /** Whether any shade is expanded, even a little bit. */
+    val isAnyShadeExpanded: Flow<Boolean> =
+        maxShadeExpansion.map { maxExpansion -> !maxExpansion.isZero() }.distinctUntilChanged()
+
     /**
      * A _processed_ version of the proxied input flow.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
new file mode 100644
index 0000000..ff7c901
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.multishade.domain.interactor
+
+import android.content.Context
+import android.view.MotionEvent
+import android.view.ViewConfiguration
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.multishade.shared.model.ProxiedInputModel
+import javax.inject.Inject
+import kotlin.math.abs
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Encapsulates business logic to handle [MotionEvent]-based user input.
+ *
+ * This class is meant purely for the legacy `View`-based system to be able to pass `MotionEvent`s
+ * into the newer multi-shade framework for processing.
+ */
+class MultiShadeMotionEventInteractor
+@Inject
+constructor(
+    @Application private val applicationContext: Context,
+    @Application private val applicationScope: CoroutineScope,
+    private val interactor: MultiShadeInteractor,
+) {
+
+    private val isAnyShadeExpanded: StateFlow<Boolean> =
+        interactor.isAnyShadeExpanded.stateIn(
+            scope = applicationScope,
+            started = SharingStarted.Eagerly,
+            initialValue = false,
+        )
+
+    private var interactionState: InteractionState? = null
+
+    /**
+     * Returns `true` if the given [MotionEvent] and the rest of events in this gesture should be
+     * passed to this interactor's [onTouchEvent] method.
+     *
+     * Note: the caller should continue to pass [MotionEvent] instances into this method, even if it
+     * returns `false` as the gesture may be intercepted mid-stream.
+     */
+    fun shouldIntercept(event: MotionEvent): Boolean {
+        if (isAnyShadeExpanded.value) {
+            // If any shade is expanded, we assume that touch handling outside the shades is handled
+            // by the scrim that appears behind the shades. No need to intercept anything here.
+            return false
+        }
+
+        return when (event.actionMasked) {
+            MotionEvent.ACTION_DOWN -> {
+                // Record where the pointer was placed and which pointer it was.
+                interactionState =
+                    InteractionState(
+                        initialX = event.x,
+                        initialY = event.y,
+                        currentY = event.y,
+                        pointerId = event.getPointerId(0),
+                        isDraggingHorizontally = false,
+                        isDraggingVertically = false,
+                    )
+
+                false
+            }
+            MotionEvent.ACTION_MOVE -> {
+                interactionState?.let {
+                    val pointerIndex = event.findPointerIndex(it.pointerId)
+                    val currentX = event.getX(pointerIndex)
+                    val currentY = event.getY(pointerIndex)
+                    if (!it.isDraggingHorizontally && !it.isDraggingVertically) {
+                        val xDistanceTravelled = abs(currentX - it.initialX)
+                        val yDistanceTravelled = abs(currentY - it.initialY)
+                        val touchSlop = ViewConfiguration.get(applicationContext).scaledTouchSlop
+                        interactionState =
+                            when {
+                                yDistanceTravelled > touchSlop ->
+                                    it.copy(isDraggingVertically = true)
+                                xDistanceTravelled > touchSlop ->
+                                    it.copy(isDraggingHorizontally = true)
+                                else -> interactionState
+                            }
+                    }
+                }
+
+                // We want to intercept the rest of the gesture if we're dragging.
+                interactionState.isDraggingVertically()
+            }
+            MotionEvent.ACTION_UP,
+            MotionEvent.ACTION_CANCEL ->
+                // Make sure that we intercept the up or cancel if we're dragging, to handle drag
+                // end and cancel.
+                interactionState.isDraggingVertically()
+            else -> false
+        }
+    }
+
+    /**
+     * Notifies that a [MotionEvent] in a series of events of a gesture that was intercepted due to
+     * the result of [shouldIntercept] has been received.
+     *
+     * @param event The [MotionEvent] to handle.
+     * @param viewWidthPx The width of the view, in pixels.
+     * @return `true` if the event was consumed, `false` otherwise.
+     */
+    fun onTouchEvent(event: MotionEvent, viewWidthPx: Int): Boolean {
+        return when (event.actionMasked) {
+            MotionEvent.ACTION_MOVE -> {
+                interactionState?.let {
+                    if (it.isDraggingVertically) {
+                        val pointerIndex = event.findPointerIndex(it.pointerId)
+                        val previousY = it.currentY
+                        val currentY = event.getY(pointerIndex)
+                        interactionState =
+                            it.copy(
+                                currentY = currentY,
+                            )
+
+                        val yDragAmountPx = currentY - previousY
+                        if (yDragAmountPx != 0f) {
+                            interactor.sendProxiedInput(
+                                ProxiedInputModel.OnDrag(
+                                    xFraction = event.x / viewWidthPx,
+                                    yDragAmountPx = yDragAmountPx,
+                                )
+                            )
+                        }
+                    }
+                }
+
+                true
+            }
+            MotionEvent.ACTION_UP -> {
+                if (interactionState.isDraggingVertically()) {
+                    // We finished dragging. Record that so the multi-shade framework can issue a
+                    // fling, if the velocity reached in the drag was high enough, for example.
+                    interactor.sendProxiedInput(ProxiedInputModel.OnDragEnd)
+                }
+
+                interactionState = null
+                true
+            }
+            MotionEvent.ACTION_CANCEL -> {
+                if (interactionState.isDraggingVertically()) {
+                    // Our drag gesture was canceled by the system. This happens primarily in one of
+                    // two occasions: (a) the parent view has decided to intercept the gesture
+                    // itself and/or route it to a different child view or (b) the pointer has
+                    // traveled beyond the bounds of our view and/or the touch display. Either way,
+                    // we pass the cancellation event to the multi-shade framework to record it.
+                    // Doing that allows the multi-shade framework to know that the gesture ended to
+                    // allow new gestures to be accepted.
+                    interactor.sendProxiedInput(ProxiedInputModel.OnDragCancel)
+                }
+
+                interactionState = null
+                true
+            }
+            else -> false
+        }
+    }
+
+    private data class InteractionState(
+        val initialX: Float,
+        val initialY: Float,
+        val currentY: Float,
+        val pointerId: Int,
+        val isDraggingHorizontally: Boolean,
+        val isDraggingVertically: Boolean,
+    )
+
+    private fun InteractionState?.isDraggingVertically(): Boolean {
+        return this?.isDraggingVertically == true
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt b/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt
new file mode 100644
index 0000000..c2eaf72
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.multishade.shared.math
+
+import androidx.annotation.VisibleForTesting
+import kotlin.math.abs
+
+/** Returns `true` if this [Float] is within [epsilon] of `0`. */
+fun Float.isZero(epsilon: Float = EPSILON): Boolean {
+    return abs(this) < epsilon
+}
+
+@VisibleForTesting private const val EPSILON = 0.0001f
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt
index ce6ab97..ed92c54 100644
--- a/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt
@@ -26,7 +26,6 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
@@ -87,10 +86,7 @@
                 when (shadeConfig) {
                     // In the dual shade configuration, the scrim is enabled when the expansion is
                     // greater than zero on any one of the shades.
-                    is ShadeConfig.DualShadeConfig ->
-                        interactor.maxShadeExpansion
-                            .map { expansion -> expansion > 0 }
-                            .distinctUntilChanged()
+                    is ShadeConfig.DualShadeConfig -> interactor.isAnyShadeExpanded
                     // No scrim in the single shade configuration.
                     is ShadeConfig.SingleShadeConfig -> flowOf(false)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index e74d78d..58ac5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -138,7 +138,8 @@
             logDebug { "onShowNoteTask - start: $info on user#${user.identifier}" }
             when (info.launchMode) {
                 is NoteTaskLaunchMode.AppBubble -> {
-                    bubbles.showOrHideAppBubble(intent, userTracker.userHandle)
+                    // TODO: provide app bubble icon
+                    bubbles.showOrHideAppBubble(intent, userTracker.userHandle, null /* icon */)
                     // App bubble logging happens on `onBubbleExpandChanged`.
                     logDebug { "onShowNoteTask - opened as app bubble: $info" }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 2668d2e..fdab9b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -91,16 +91,19 @@
             new QSPanel.OnConfigurationChangedListener() {
                 @Override
                 public void onConfigurationChange(Configuration newConfig) {
-                    mQSLogger.logOnConfigurationChanged(
-                        /* lastOrientation= */ mLastOrientation,
-                        /* newOrientation= */ newConfig.orientation,
-                        /* containerName= */ mView.getDumpableTag());
-
-                    boolean previousSplitShadeState = mShouldUseSplitNotificationShade;
+                    final boolean previousSplitShadeState = mShouldUseSplitNotificationShade;
+                    final int previousOrientation = mLastOrientation;
                     mShouldUseSplitNotificationShade =
-                        LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+                            LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
                     mLastOrientation = newConfig.orientation;
 
+                    mQSLogger.logOnConfigurationChanged(
+                        /* oldOrientation= */ previousOrientation,
+                        /* newOrientation= */ mLastOrientation,
+                        /* oldShouldUseSplitShade= */ previousSplitShadeState,
+                        /* newShouldUseSplitShade= */ mShouldUseSplitNotificationShade,
+                        /* containerName= */ mView.getDumpableTag());
+
                     switchTileLayoutIfNeeded();
                     onConfigurationChanged();
                     if (previousSplitShadeState != mShouldUseSplitNotificationShade) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 2083cc7..5e4f531 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -136,9 +136,8 @@
             mServices.remove(tile);
             mTokenMap.remove(service.getToken());
             mTiles.remove(tile.getComponent());
-            final String slot = tile.getComponent().getClassName();
-            // TileServices doesn't know how to add more than 1 icon per slot, so remove all
-            mMainHandler.post(() -> mStatusBarIconController.removeAllIconsForSlot(slot));
+            final String slot = getStatusBarIconSlotName(tile.getComponent());
+            mMainHandler.post(() -> mStatusBarIconController.removeIconForTile(slot));
         }
     }
 
@@ -312,12 +311,11 @@
                             ? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
                                     contentDescription)
                             : null;
+                    final String slot = getStatusBarIconSlotName(componentName);
                     mMainHandler.post(new Runnable() {
                         @Override
                         public void run() {
-                            StatusBarIconController iconController = mStatusBarIconController;
-                            iconController.setIcon(componentName.getClassName(), statusIcon);
-                            iconController.setExternalIcon(componentName.getClassName());
+                            mStatusBarIconController.setIconFromTile(slot, statusIcon);
                         }
                     });
                 }
@@ -377,6 +375,12 @@
         mCommandQueue.removeCallback(mRequestListeningCallback);
     }
 
+    /** Returns the slot name that should be used when adding or removing status bar icons. */
+    private String getStatusBarIconSlotName(ComponentName componentName) {
+        return componentName.getClassName();
+    }
+
+
     private final CommandQueue.Callbacks mRequestListeningCallback = new CommandQueue.Callbacks() {
         @Override
         public void requestTileServiceListeningState(@NonNull ComponentName componentName) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 23c41db..5b461a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -16,8 +16,12 @@
 
 package com.android.systemui.qs.logging
 
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.content.res.Configuration.Orientation
 import android.service.quicksettings.Tile
 import android.view.View
+import com.android.systemui.log.dagger.QSConfigLog
 import com.android.systemui.log.dagger.QSLog
 import com.android.systemui.plugins.log.ConstantStringsLogger
 import com.android.systemui.plugins.log.ConstantStringsLoggerImpl
@@ -32,8 +36,12 @@
 
 private const val TAG = "QSLog"
 
-class QSLogger @Inject constructor(@QSLog private val buffer: LogBuffer) :
-    ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) {
+class QSLogger
+@Inject
+constructor(
+    @QSLog private val buffer: LogBuffer,
+    @QSConfigLog private val configChangedBuffer: LogBuffer,
+) : ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) {
 
     fun logException(@CompileTimeConstant logMsg: String, ex: Exception) {
         buffer.log(TAG, ERROR, {}, { logMsg }, exception = ex)
@@ -264,19 +272,28 @@
     }
 
     fun logOnConfigurationChanged(
-        lastOrientation: Int,
-        newOrientation: Int,
+        @Orientation oldOrientation: Int,
+        @Orientation newOrientation: Int,
+        newShouldUseSplitShade: Boolean,
+        oldShouldUseSplitShade: Boolean,
         containerName: String
     ) {
-        buffer.log(
+        configChangedBuffer.log(
             TAG,
             DEBUG,
             {
                 str1 = containerName
-                int1 = lastOrientation
+                int1 = oldOrientation
                 int2 = newOrientation
+                bool1 = oldShouldUseSplitShade
+                bool2 = newShouldUseSplitShade
             },
-            { "configuration change: $str1 orientation was $int1, now $int2" }
+            {
+                "config change: " +
+                    "$str1 orientation=${toOrientationString(int2)} " +
+                    "(was ${toOrientationString(int1)}), " +
+                    "splitShade=$bool2 (was $bool1)"
+            }
         )
     }
 
@@ -353,3 +370,11 @@
         }
     }
 }
+
+private inline fun toOrientationString(@Orientation orientation: Int): String {
+    return when (orientation) {
+        ORIENTATION_LANDSCAPE -> "land"
+        ORIENTATION_PORTRAIT -> "port"
+        else -> "undefined"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 557e95c..7ad594e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -115,6 +115,8 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
@@ -613,11 +615,13 @@
         // Note that this may block if the sound is still being loaded (very unlikely) but we can't
         // reliably release in the background because the service is being destroyed.
         try {
-            MediaPlayer player = mCameraSound.get();
+            MediaPlayer player = mCameraSound.get(1, TimeUnit.SECONDS);
             if (player != null) {
                 player.release();
             }
-        } catch (InterruptedException | ExecutionException e) {
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            mCameraSound.cancel(true);
+            Log.w(TAG, "Error releasing shutter sound", e);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
index 48aa60f..253f07d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.screenshot
 
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ComponentInfoFlags
+import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
 import android.view.Display
 import android.view.IWindowManager
 import android.view.ViewGroup
@@ -45,7 +47,7 @@
         // Convert component names to app names.
         return components.map {
             packageManager
-                .getActivityInfo(it, PackageManager.ComponentInfoFlags.of(0))
+                .getActivityInfo(it, ComponentInfoFlags.of(MATCH_DISABLED_COMPONENTS.toLong()))
                 .loadLabel(packageManager)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 1c3e011..3360511 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -159,6 +159,7 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.ClockAnimations;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
 import com.android.systemui.plugins.qs.QS;
@@ -1592,10 +1593,9 @@
                 transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
                 transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
 
-                boolean customClockAnimation =
-                            mKeyguardStatusViewController.getClockAnimations() != null
-                            && mKeyguardStatusViewController.getClockAnimations()
-                                    .getHasCustomPositionUpdatedAnimation();
+                ClockAnimations clockAnims = mKeyguardStatusViewController.getClockAnimations();
+                boolean customClockAnimation = clockAnims != null
+                        && clockAnims.getHasCustomPositionUpdatedAnimation();
 
                 if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
                     // Find the clock, so we can exclude it from this transition.
@@ -2897,15 +2897,7 @@
         mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
         mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
                 mNotificationStackScrollLayoutController.getHeadsUpCallback(),
-                NotificationPanelViewController.this);
-    }
-
-    public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
-        if (pickedChild != null) {
-            updateTrackingHeadsUp(pickedChild);
-            mExpandingFromHeadsUp = true;
-        }
-        // otherwise we update the state when the expansion is finished
+                new HeadsUpNotificationViewControllerImpl());
     }
 
     private void onClosingFinished() {
@@ -2953,7 +2945,8 @@
     }
 
     /** Called when a HUN is dragged up or down to indicate the starting height for shade motion. */
-    public void setHeadsUpDraggingStartingHeight(int startHeight) {
+    @VisibleForTesting
+    void setHeadsUpDraggingStartingHeight(int startHeight) {
         mHeadsUpStartHeight = startHeight;
         float scrimMinFraction;
         if (mSplitShadeEnabled) {
@@ -2987,10 +2980,6 @@
         mScrimController.setPanelScrimMinFraction(mMinFraction);
     }
 
-    public void clearNotificationEffects() {
-        mCentralSurfaces.clearNotificationEffects();
-    }
-
     private boolean isPanelVisibleBecauseOfHeadsUp() {
         return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
                 && mBarState == StatusBarState.SHADE;
@@ -3629,7 +3618,13 @@
                             : (mKeyguardStateController.canDismissLockScreen()
                                     ? UNLOCK : BOUNCER_UNLOCK);
 
-            fling(vel, expand, isFalseTouch(x, y, interactionType));
+            // don't fling while in keyguard to avoid jump in shade expand animation;
+            // touch has been intercepted already so flinging here is redundant
+            if (mBarState == KEYGUARD && mExpandedFraction >= 1.0) {
+                mShadeLog.d("NPVC endMotionEvent - skipping fling on keyguard");
+            } else {
+                fling(vel, expand, isFalseTouch(x, y, interactionType));
+            }
             onTrackingStopped(expand);
             mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
             if (mUpdateFlingOnLayout) {
@@ -5112,17 +5107,26 @@
             captureValues(transitionValues);
         }
 
+        @Nullable
         @Override
-        public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-                TransitionValues endValues) {
+        public Animator createAnimator(ViewGroup sceneRoot, @Nullable TransitionValues startValues,
+                @Nullable TransitionValues endValues) {
+            if (startValues == null || endValues == null) {
+                return null;
+            }
             ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
 
             Rect from = (Rect) startValues.values.get(PROP_BOUNDS);
             Rect to = (Rect) endValues.values.get(PROP_BOUNDS);
 
-            anim.addUpdateListener(
-                    animation -> mController.getClockAnimations().onPositionUpdated(
-                            from, to, animation.getAnimatedFraction()));
+            anim.addUpdateListener(animation -> {
+                ClockAnimations clockAnims = mController.getClockAnimations();
+                if (clockAnims == null) {
+                    return;
+                }
+
+                clockAnims.onPositionUpdated(from, to, animation.getAnimatedFraction());
+            });
 
             return anim;
         }
@@ -5133,6 +5137,33 @@
         }
     }
 
+    private final class HeadsUpNotificationViewControllerImpl implements
+            HeadsUpTouchHelper.HeadsUpNotificationViewController {
+        @Override
+        public void setHeadsUpDraggingStartingHeight(int startHeight) {
+            NotificationPanelViewController.this.setHeadsUpDraggingStartingHeight(startHeight);
+        }
+
+        @Override
+        public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
+            if (pickedChild != null) {
+                updateTrackingHeadsUp(pickedChild);
+                mExpandingFromHeadsUp = true;
+            }
+            // otherwise we update the state when the expansion is finished
+        }
+
+        @Override
+        public void startExpand(float x, float y, boolean startTracking, float expandedHeight) {
+            startExpandMotion(x, y, startTracking, expandedHeight);
+        }
+
+        @Override
+        public void clearNotificationEffects() {
+            mCentralSurfaces.clearNotificationEffects();
+        }
+    }
+
     private final class ShadeAccessibilityDelegate extends AccessibilityDelegate {
         @Override
         public void onInitializeAccessibilityNodeInfo(View host,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 5f6f158..0318fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -32,6 +32,8 @@
 import android.view.ViewGroup;
 import android.view.ViewStub;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.AuthKeyguardMessageArea;
 import com.android.keyguard.LockIconViewController;
@@ -50,6 +52,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor;
+import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor;
 import com.android.systemui.multishade.ui.view.MultiShadeView;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -118,6 +121,7 @@
                     step.getTransitionState() == TransitionState.RUNNING;
             };
     private final SystemClock mClock;
+    private final @Nullable MultiShadeMotionEventInteractor mMultiShadeMotionEventInteractor;
 
     @Inject
     public NotificationShadeWindowViewController(
@@ -145,7 +149,8 @@
             PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
             FeatureFlags featureFlags,
             Provider<MultiShadeInteractor> multiShadeInteractorProvider,
-            SystemClock clock) {
+            SystemClock clock,
+            Provider<MultiShadeMotionEventInteractor> multiShadeMotionEventInteractorProvider) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
         mStatusBarStateController = statusBarStateController;
@@ -180,22 +185,18 @@
         mClock = clock;
         if (ComposeFacade.INSTANCE.isComposeAvailable()
                 && featureFlags.isEnabled(Flags.DUAL_SHADE)) {
+            mMultiShadeMotionEventInteractor = multiShadeMotionEventInteractorProvider.get();
             final ViewStub multiShadeViewStub = mView.findViewById(R.id.multi_shade_stub);
             if (multiShadeViewStub != null) {
                 final MultiShadeView multiShadeView = (MultiShadeView) multiShadeViewStub.inflate();
                 multiShadeView.init(multiShadeInteractorProvider.get(), clock);
             }
+        } else {
+            mMultiShadeMotionEventInteractor = null;
         }
     }
 
     /**
-     * @return Location where to place the KeyguardBouncer
-     */
-    public ViewGroup getBouncerContainer() {
-        return mView.findViewById(R.id.keyguard_bouncer_container);
-    }
-
-    /**
      * @return Location where to place the KeyguardMessageArea
      */
     public AuthKeyguardMessageArea getKeyguardMessageArea() {
@@ -349,16 +350,17 @@
                     return true;
                 }
 
-                boolean intercept = false;
-                if (mNotificationPanelViewController.isFullyExpanded()
+                if (mMultiShadeMotionEventInteractor != null) {
+                    // This interactor is not null only if the dual shade feature is enabled.
+                    return mMultiShadeMotionEventInteractor.shouldIntercept(ev);
+                } else if (mNotificationPanelViewController.isFullyExpanded()
                         && mDragDownHelper.isDragDownEnabled()
                         && !mService.isBouncerShowing()
                         && !mStatusBarStateController.isDozing()) {
-                    intercept = mDragDownHelper.onInterceptTouchEvent(ev);
+                    return mDragDownHelper.onInterceptTouchEvent(ev);
+                } else {
+                    return false;
                 }
-
-                return intercept;
-
             }
 
             @Override
@@ -381,13 +383,20 @@
                     return true;
                 }
 
-                if ((mDragDownHelper.isDragDownEnabled() && !handled)
-                        || mDragDownHelper.isDraggingDown()) {
-                    // we still want to finish our drag down gesture when locking the screen
-                    handled = mDragDownHelper.onTouchEvent(ev);
+                if (handled) {
+                    return true;
                 }
 
-                return handled;
+                if (mMultiShadeMotionEventInteractor != null) {
+                    // This interactor is not null only if the dual shade feature is enabled.
+                    return mMultiShadeMotionEventInteractor.onTouchEvent(ev, mView.getWidth());
+                } else if (mDragDownHelper.isDragDownEnabled()
+                        || mDragDownHelper.isDraggingDown()) {
+                    // we still want to finish our drag down gesture when locking the screen
+                    return mDragDownHelper.onTouchEvent(ev);
+                } else {
+                    return false;
+                }
             }
 
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 5ba8801..bfb6416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -94,7 +94,11 @@
         /**
          * No conditions blocking FSI launch.
          */
-        FSI_EXPECTED_NOT_TO_HUN(true);
+        FSI_EXPECTED_NOT_TO_HUN(true),
+        /**
+         * The notification is coming from a suspended packages, so FSI is suppressed.
+         */
+        NO_FSI_SUSPENDED(false);
 
         public final boolean shouldLaunch;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 6f4eed3..4aaa7ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -28,7 +28,6 @@
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Handler;
 import android.os.PowerManager;
-import android.os.SystemProperties;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 
@@ -274,6 +273,12 @@
                     suppressedByDND);
         }
 
+        // Notification is coming from a suspended package, block FSI
+        if (entry.getRanking().isSuspended()) {
+            return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_SUSPENDED,
+                    suppressedByDND);
+        }
+
         // If the screen is off, then launch the FullScreenIntent
         if (!mPowerManager.isInteractive()) {
             return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 55fa479..7f8c135 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -217,8 +217,6 @@
 
     NotificationPanelViewController getNotificationPanelViewController();
 
-    ViewGroup getBouncerContainer();
-
     /** Get the Keyguard Message Area that displays auth messages. */
     AuthKeyguardMessageArea getKeyguardMessageArea();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 5e3c1c5..2f40487 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1722,11 +1722,6 @@
     }
 
     @Override
-    public ViewGroup getBouncerContainer() {
-        return mNotificationShadeWindowViewController.getBouncerContainer();
-    }
-
-    @Override
     public AuthKeyguardMessageArea getKeyguardMessageArea() {
         return mNotificationShadeWindowViewController.getKeyguardMessageArea();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 90d0b69..16c2e36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,7 +21,6 @@
 import android.view.ViewConfiguration;
 
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -31,21 +30,21 @@
  */
 public class HeadsUpTouchHelper implements Gefingerpoken {
 
-    private HeadsUpManagerPhone mHeadsUpManager;
-    private Callback mCallback;
+    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final Callback mCallback;
     private int mTrackingPointer;
-    private float mTouchSlop;
+    private final float mTouchSlop;
     private float mInitialTouchX;
     private float mInitialTouchY;
     private boolean mTouchingHeadsUpView;
     private boolean mTrackingHeadsUp;
     private boolean mCollapseSnoozes;
-    private NotificationPanelViewController mPanel;
+    private final HeadsUpNotificationViewController mPanel;
     private ExpandableNotificationRow mPickedChild;
 
     public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
             Callback callback,
-            NotificationPanelViewController notificationPanelView) {
+            HeadsUpNotificationViewController notificationPanelView) {
         mHeadsUpManager = headsUpManager;
         mCallback = callback;
         mPanel = notificationPanelView;
@@ -116,7 +115,7 @@
                     int startHeight = (int) (mPickedChild.getActualHeight()
                                                 + mPickedChild.getTranslationY());
                     mPanel.setHeadsUpDraggingStartingHeight(startHeight);
-                    mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
+                    mPanel.startExpand(x, y, true /* startTracking */, startHeight);
                     // This call needs to be after the expansion start otherwise we will get a
                     // flicker of one frame as it's not expanded yet.
                     mHeadsUpManager.unpinAll(true);
@@ -181,4 +180,19 @@
         boolean isExpanded();
         Context getContext();
     }
+
+    /** The controller for a view that houses heads up notifications. */
+    public interface HeadsUpNotificationViewController {
+        /** Called when a HUN is dragged to indicate the starting height for shade motion. */
+        void setHeadsUpDraggingStartingHeight(int startHeight);
+
+        /** Sets notification that is being expanded. */
+        void setTrackedHeadsUp(ExpandableNotificationRow expandableNotificationRow);
+
+        /** Called when a MotionEvent is about to trigger expansion. */
+        void startExpand(float newX, float newY, boolean startTracking, float expandedHeight);
+
+        /** Clear any effects that were added for the expansion. */
+        void clearNotificationEffects();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 04cc8ce..30d2295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -81,28 +81,22 @@
     void refreshIconGroup(IconManager iconManager);
 
     /**
-     * Adds or updates an icon for a given slot for a **tile service icon**.
+     * Adds or updates an icon that comes from an active tile service.
      *
-     * TODO(b/265307726): Merge with {@link #setIcon(String, StatusBarIcon)} or make this method
-     *   much more clearly distinct from that method.
+     * If the icon is null, the icon will be removed.
      */
-    void setExternalIcon(String slot);
+    void setIconFromTile(String slot, @Nullable StatusBarIcon icon);
+
+    /** Removes an icon that had come from an active tile service. */
+    void removeIconForTile(String slot);
 
     /**
      * Adds or updates an icon for the given slot for **internal system icons**.
      *
-     * TODO(b/265307726): Rename to `setInternalIcon`, or merge this appropriately with the
-     * {@link #setIcon(String, StatusBarIcon)} method.
+     * TODO(b/265307726): Re-name this to `setInternalIcon`.
      */
     void setIcon(String slot, int resourceId, CharSequence contentDescription);
 
-    /**
-     * Adds or updates an icon for the given slot for an **externally-provided icon**.
-     *
-     * TODO(b/265307726): Rename to `setExternalIcon` or something similar.
-     */
-    void setIcon(String slot, StatusBarIcon icon);
-
     /** */
     void setWifiIcon(String slot, WifiIconState state);
 
@@ -152,15 +146,10 @@
      */
     void removeIcon(String slot, int tag);
 
-    /** */
-    void removeAllIconsForSlot(String slot);
-
     /**
-     * Removes all the icons for the given slot.
-     *
-     * Only use this for icons that have come from **an external process**.
+     * TODO(b/265307726): Re-name this to `removeAllIconsForInternalSlot`.
      */
-    void removeAllIconsForExternalSlot(String slot);
+    void removeAllIconsForSlot(String slot);
 
     // TODO: See if we can rename this tunable name.
     String ICON_HIDE_LIST = "icon_blacklist";
@@ -618,13 +607,6 @@
             mGroup.removeAllViews();
         }
 
-        protected void onIconExternal(int viewIndex, int height) {
-            ImageView imageView = (ImageView) mGroup.getChildAt(viewIndex);
-            imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
-            imageView.setAdjustViewBounds(true);
-            setHeightAndCenter(imageView, height);
-        }
-
         protected void onDensityOrFontScaleChanged() {
             for (int i = 0; i < mGroup.getChildCount(); i++) {
                 View child = mGroup.getChildAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 0727c5a..3a18423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -32,7 +32,6 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.Dumpable;
-import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
@@ -62,7 +61,7 @@
  */
 @SysUISingleton
 public class StatusBarIconControllerImpl implements Tunable,
-        ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController, DemoMode {
+        ConfigurationListener, Dumpable, StatusBarIconController, DemoMode {
 
     private static final String TAG = "StatusBarIconController";
     // Use this suffix to prevent external icon slot names from unintentionally overriding our
@@ -93,7 +92,7 @@
         mStatusBarPipelineFlags = statusBarPipelineFlags;
 
         configurationController.addCallback(this);
-        commandQueue.addCallback(this);
+        commandQueue.addCallback(mCommandQueueCallbacks);
         tunerService.addTunable(this, ICON_HIDE_LIST);
         demoModeController.addCallback(this);
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
@@ -350,26 +349,35 @@
         }
     }
 
-    @Override
-    public void setExternalIcon(String slot) {
-        String slotName = createExternalSlotName(slot);
-        int viewIndex = mStatusBarIconList.getViewIndex(slotName, 0);
-        int height = mContext.getResources().getDimensionPixelSize(
-                R.dimen.status_bar_icon_drawing_size);
-        mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height));
-    }
-
-    // Override for *both* CommandQueue.Callbacks AND StatusBarIconController.
-    // TODO(b/265307726): Pull out the CommandQueue callbacks into a member variable to
-    //  differentiate between those callback methods and StatusBarIconController methods.
-    @Override
-    public void setIcon(String slot, StatusBarIcon icon) {
-        String slotName = createExternalSlotName(slot);
-        if (icon == null) {
-            removeAllIconsForSlot(slotName);
-            return;
+    private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
+        @Override
+        public void setIcon(String slot, StatusBarIcon icon) {
+            // Icons that come from CommandQueue are from external services.
+            setExternalIcon(slot, icon);
         }
 
+        @Override
+        public void removeIcon(String slot) {
+            removeAllIconsForExternalSlot(slot);
+        }
+    };
+
+    @Override
+    public void setIconFromTile(String slot, StatusBarIcon icon) {
+        setExternalIcon(slot, icon);
+    }
+
+    @Override
+    public void removeIconForTile(String slot) {
+        removeAllIconsForExternalSlot(slot);
+    }
+
+    private void setExternalIcon(String slot, StatusBarIcon icon) {
+        if (icon == null) {
+            removeAllIconsForExternalSlot(slot);
+            return;
+        }
+        String slotName = createExternalSlotName(slot);
         StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon);
         setIcon(slotName, holder);
     }
@@ -417,14 +425,6 @@
         }
     }
 
-    // CommandQueue.Callbacks override
-    // TODO(b/265307726): Pull out the CommandQueue callbacks into a member variable to
-    //  differentiate between those callback methods and StatusBarIconController methods.
-    @Override
-    public void removeIcon(String slot) {
-        removeAllIconsForExternalSlot(slot);
-    }
-
     /** */
     @Override
     public void removeIcon(String slot, int tag) {
@@ -444,8 +444,7 @@
         mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
     }
 
-    @Override
-    public void removeAllIconsForExternalSlot(String slotName) {
+    private void removeAllIconsForExternalSlot(String slotName) {
         removeAllIconsForSlot(createExternalSlotName(slotName));
     }
 
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 06d0758..f06b5db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -381,7 +381,6 @@
         mCentralSurfaces = centralSurfaces;
         mBiometricUnlockController = biometricUnlockController;
 
-        ViewGroup container = mCentralSurfaces.getBouncerContainer();
         mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
         mNotificationPanelViewController = notificationPanelViewController;
         if (shadeExpansionStateManager != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index ed978c3..24ddded 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -453,9 +453,6 @@
     protected int adjustDisableFlags(int state) {
         boolean headsUpVisible =
                 mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
-        if (headsUpVisible) {
-            state |= DISABLE_CLOCK;
-        }
 
         if (!mKeyguardStateController.isLaunchTransitionFadingAway()
                 && !mKeyguardStateController.isKeyguardFadingAway()
@@ -473,6 +470,13 @@
             state |= DISABLE_ONGOING_CALL_CHIP;
         }
 
+        if (headsUpVisible) {
+            // Disable everything on the left side of the status bar, since the app name for the
+            // heads up notification appears there instead.
+            state |= DISABLE_CLOCK;
+            state |= DISABLE_ONGOING_CALL_CHIP;
+        }
+
         return state;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 4156fc1..73bf188 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -179,6 +179,10 @@
     fun logDefaultMobileIconGroup(group: SignalIcon.MobileIconGroup) {
         buffer.log(TAG, LogLevel.INFO, { str1 = group.name }, { "defaultMobileIconGroup: $str1" })
     }
+
+    fun logOnSubscriptionsChanged() {
+        buffer.log(TAG, LogLevel.INFO, {}, { "onSubscriptionsChanged" })
+    }
 }
 
 private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index b7da3f2..991b786 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -129,6 +129,7 @@
         val callback =
             object : SubscriptionManager.OnSubscriptionsChangedListener() {
                 override fun onSubscriptionsChanged() {
+                    logger.logOnSubscriptionsChanged()
                     trySend(Unit)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 805368c..f1269f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -398,6 +398,7 @@
         pw.println("  mFaceAuthEnabled: " + mFaceAuthEnabled);
         pw.println("  isKeyguardFadingAway: " + isKeyguardFadingAway());
         pw.println("  isKeyguardGoingAway: " + isKeyguardGoingAway());
+        pw.println("  isLaunchTransitionFadingAway: " + isLaunchTransitionFadingAway());
     }
 
     private class UpdateMonitorCallback extends KeyguardUpdateMonitorCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index 9952cfd..3805019 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -261,22 +261,26 @@
     private fun trackAndLogUsiSession(deviceId: Int, batteryStateValid: Boolean) {
         // TODO(b/268618918) handle cases where an invalid battery callback from a previous stylus
         //  is sent after the actual valid callback
+        val hasBtConnection = if (inputDeviceBtSessionIdMap.isEmpty()) 0 else 1
+
         if (batteryStateValid && usiSessionId == null) {
             logDebug { "USI battery newly present, entering new USI session: $deviceId" }
             usiSessionId = instanceIdSequence.newInstanceId()
-            uiEventLogger.logWithInstanceId(
+            uiEventLogger.logWithInstanceIdAndPosition(
                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
                 0,
                 null,
-                usiSessionId
+                usiSessionId,
+                hasBtConnection,
             )
         } else if (!batteryStateValid && usiSessionId != null) {
             logDebug { "USI battery newly absent, exiting USI session: $deviceId" }
-            uiEventLogger.logWithInstanceId(
+            uiEventLogger.logWithInstanceIdAndPosition(
                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED,
                 0,
                 null,
-                usiSessionId
+                usiSessionId,
+                hasBtConnection,
             )
             usiSessionId = null
         }
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 125cc76..6e58f22 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -18,7 +18,8 @@
 
 import android.os.VibrationEffect
 import android.view.View
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
+import com.android.systemui.R
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.shared.model.TintedIcon
 import com.android.systemui.temporarydisplay.TemporaryViewInfo
@@ -48,7 +49,7 @@
     override val priority: ViewPriority,
 ) : TemporaryViewInfo() {
     companion object {
-        @AttrRes const val DEFAULT_ICON_TINT_ATTR = android.R.attr.textColorPrimary
+        @ColorRes val DEFAULT_ICON_TINT = R.color.chipbar_text_and_icon_color
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
index 460b7d9..a5828c7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
@@ -23,6 +23,8 @@
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.Execution;
 
+import java.util.HashSet;
+
 import javax.inject.Inject;
 
 /**
@@ -37,6 +39,7 @@
     private final ThresholdSensor[] mPostureToPrimaryProxSensorMap;
     private final ThresholdSensor[] mPostureToSecondaryProxSensorMap;
 
+    private final HashSet<Listener> mListenersRegisteredWhenProxUnavailable = new HashSet<>();
     private final DevicePostureController mDevicePostureController;
 
     @Inject
@@ -69,6 +72,25 @@
         mDevicePostureController.removeCallback(mDevicePostureCallback);
     }
 
+    @Override
+    public void register(ThresholdSensor.Listener listener) {
+        if (!isLoaded()) {
+            logDebug("No prox sensor when registering listener=" + listener);
+            mListenersRegisteredWhenProxUnavailable.add(listener);
+        }
+
+        super.register(listener);
+    }
+
+    @Override
+    public void unregister(ThresholdSensor.Listener listener) {
+        if (mListenersRegisteredWhenProxUnavailable.remove(listener)) {
+            logDebug("Removing listener from mListenersRegisteredWhenProxUnavailable "
+                    + listener);
+        }
+        super.unregister(listener);
+    }
+
     private void chooseSensors() {
         if (mDevicePosture >= mPostureToPrimaryProxSensorMap.length
                 || mDevicePosture >= mPostureToSecondaryProxSensorMap.length) {
@@ -98,6 +120,14 @@
 
             mInitializedListeners = false;
             registerInternal();
+
+            final Listener[] listenersToReregister =
+                    mListenersRegisteredWhenProxUnavailable.toArray(new Listener[0]);
+            mListenersRegisteredWhenProxUnavailable.clear();
+            for (Listener listener : listenersToReregister) {
+                logDebug("Re-register listener " + listener);
+                register(listener);
+            }
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index a5f90f8..b15ac39 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
@@ -365,6 +366,12 @@
         assertEquals(View.VISIBLE, mFakeWeatherView.getVisibility());
     }
 
+    @Test
+    public void testGetClockAnimations_nullClock_returnsNull() {
+        when(mClockEventController.getClock()).thenReturn(null);
+        assertNull(mController.getClockAnimations());
+    }
+
     private void verifyAttachment(VerificationMode times) {
         verify(mClockRegistry, times).registerClockChangeListener(
                 any(ClockRegistry.ClockChangeListener.class));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index f966eb3..b73330f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -196,6 +196,7 @@
                 .thenReturn(mKeyguardMessageAreaController);
         when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
         when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN);
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
         mKeyguardPasswordViewController = new KeyguardPasswordViewController(
                 (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
                 SecurityMode.Password, mLockPatternUtils, null,
@@ -554,6 +555,22 @@
     }
 
     @Test
+    public void testSecurityCallbackFinish() {
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUserUnlocked(0)).thenReturn(true);
+        mKeyguardSecurityContainerController.finish(true, 0);
+        verify(mViewMediatorCallback).keyguardDone(anyBoolean(), anyInt());
+    }
+
+    @Test
+    public void testSecurityCallbackFinish_cannotDismissLockScreenAndNotStrongAuth() {
+        when(mFeatureFlags.isEnabled(Flags.PREVENT_BYPASS_KEYGUARD)).thenReturn(true);
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        mKeyguardSecurityContainerController.finish(false, 0);
+        verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt());
+    }
+
+    @Test
     public void testOnStartingToHide() {
         mKeyguardSecurityContainerController.onStartingToHide();
         verify(mInputViewController).onStartingToHide();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 86ba30c..fb21db7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2256,6 +2256,26 @@
     }
 
     @Test
+    public void assistantVisible_requestActiveUnlock() {
+        // GIVEN active unlock requests from the assistant are allowed
+        when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.ASSISTANT)).thenReturn(true);
+
+        // GIVEN should trigger active unlock
+        keyguardIsVisible();
+        keyguardNotGoingAway();
+        statusBarShadeIsNotLocked();
+        when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+        // WHEN the assistant is visible
+        mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+        // THEN request unlock with keyguard dismissal
+        verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+                eq(true));
+    }
+
+    @Test
     public void fingerprintFailure_requestActiveUnlock_dismissKeyguard()
             throws RemoteException {
         // GIVEN shouldTriggerActiveUnlock
@@ -2489,6 +2509,57 @@
     }
 
     @Test
+    public void unfoldFromPostureChange_requestActiveUnlock_forceDismissKeyguard()
+            throws RemoteException {
+        // GIVEN shouldTriggerActiveUnlock
+        keyguardIsVisible();
+        when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+        // GIVEN active unlock triggers on wakeup
+        when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
+                .thenReturn(true);
+
+        // GIVEN an unfold should force dismiss the keyguard
+        when(mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(
+                PowerManager.WAKE_REASON_UNFOLD_DEVICE)).thenReturn(true);
+
+        // WHEN device posture changes to unfold
+        deviceInPostureStateOpened();
+        mTestableLooper.processAllMessages();
+
+        // THEN request unlock with a keyguard dismissal
+        verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+                eq(true));
+    }
+
+
+    @Test
+    public void unfoldFromPostureChange_requestActiveUnlock_noDismissKeyguard()
+            throws RemoteException {
+        // GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
+        keyguardIsVisible();
+        when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+        // GIVEN active unlock triggers on wakeup
+        when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
+                .thenReturn(true);
+
+        // GIVEN an unfold should NOT force dismiss the keyguard
+        when(mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(
+                PowerManager.WAKE_REASON_UNFOLD_DEVICE)).thenReturn(false);
+
+        // WHEN device posture changes to unfold
+        deviceInPostureStateOpened();
+        mTestableLooper.processAllMessages();
+
+        // THEN request unlock WITHOUT a keyguard dismissal
+        verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+                eq(false));
+    }
+
+    @Test
     public void detectFingerprint_onTemporaryLockoutReset_authenticateFingerprint() {
         ArgumentCaptor<FingerprintManager.LockoutResetCallback> fpLockoutResetCallbackCaptor =
                 ArgumentCaptor.forClass(FingerprintManager.LockoutResetCallback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index 21191db..0ab675c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -62,6 +62,7 @@
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -98,7 +99,6 @@
 
     @JvmField @Rule var rule = MockitoJUnit.rule()
 
-    @Mock lateinit var keyguardStateController: KeyguardStateController
     @Mock lateinit var layoutInflater: LayoutInflater
     @Mock lateinit var fingerprintManager: FingerprintManager
     @Mock lateinit var windowManager: WindowManager
@@ -138,7 +138,8 @@
         keyguardBouncerRepository = FakeKeyguardBouncerRepository()
         alternateBouncerInteractor =
             AlternateBouncerInteractor(
-                keyguardStateController,
+                mock(StatusBarStateController::class.java),
+                mock(KeyguardStateController::class.java),
                 keyguardBouncerRepository,
                 FakeBiometricSettingsRepository(),
                 FakeDeviceEntryFingerprintAuthRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
index 786cb01..cefa9b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -93,6 +94,7 @@
             )
         mAlternateBouncerInteractor =
             AlternateBouncerInteractor(
+                mock(StatusBarStateController::class.java),
                 mock(KeyguardStateController::class.java),
                 keyguardBouncerRepository,
                 mock(BiometricSettingsRepository::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 10757ae..5b3e518 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -201,6 +201,56 @@
     }
 
     @Test
+    fun testSingleAppHeaderIsNotClickable() {
+        mockLayoutInflater()
+        val packageName = "pkg"
+        `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
+        val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
+        val serviceInfo = setUpPanel(panel)
+
+        underTest.show(parent, {}, context)
+
+        val captor = argumentCaptor<ControlsListingController.ControlsListingCallback>()
+
+        verify(controlsListingController).addCallback(capture(captor))
+
+        captor.value.onServicesUpdated(listOf(serviceInfo))
+        FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
+
+        val header: View = parent.requireViewById(R.id.controls_header)
+        assertThat(header.isClickable).isFalse()
+        assertThat(header.hasOnClickListeners()).isFalse()
+    }
+
+    @Test
+    fun testMultipleAppHeaderIsClickable() {
+        mockLayoutInflater()
+        val packageName1 = "pkg"
+        val panel1 = SelectedItem.PanelItem("App name 1", ComponentName(packageName1, "cls"))
+        val serviceInfo1 = setUpPanel(panel1)
+
+        val packageName2 = "pkg"
+        val panel2 = SelectedItem.PanelItem("App name 2", ComponentName(packageName2, "cls"))
+        val serviceInfo2 = setUpPanel(panel2)
+
+        `when`(authorizedPanelsRepository.getAuthorizedPanels())
+                .thenReturn(setOf(packageName1, packageName2))
+
+        underTest.show(parent, {}, context)
+
+        val captor = argumentCaptor<ControlsListingController.ControlsListingCallback>()
+
+        verify(controlsListingController).addCallback(capture(captor))
+
+        captor.value.onServicesUpdated(listOf(serviceInfo1, serviceInfo2))
+        FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
+
+        val header: View = parent.requireViewById(R.id.controls_header)
+        assertThat(header.isClickable).isTrue()
+        assertThat(header.hasOnClickListeners()).isTrue()
+    }
+
+    @Test
     fun testPanelControllerStartActivityWithCorrectArguments() {
         mockLayoutInflater()
         val packageName = "pkg"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
index 19347c7..58eb7d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
@@ -21,9 +21,11 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.DreamManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -50,6 +52,9 @@
     @Mock
     Condition.Callback mCallback;
 
+    @Mock
+    DreamManager mDreamManager;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -59,29 +64,39 @@
      * Ensure a dreaming state immediately triggers the condition.
      */
     @Test
-    public void testInitialState() {
-        final Intent intent = new Intent(Intent.ACTION_DREAMING_STARTED);
-        when(mContext.registerReceiver(any(), any())).thenReturn(intent);
-        final DreamCondition condition = new DreamCondition(mContext);
+    public void testInitialDreamingState() {
+        when(mDreamManager.isDreaming()).thenReturn(true);
+        final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
         condition.addCallback(mCallback);
-        condition.start();
 
         verify(mCallback).onConditionChanged(eq(condition));
         assertThat(condition.isConditionMet()).isTrue();
     }
 
     /**
+     * Ensure a non-dreaming state does not trigger the condition.
+     */
+    @Test
+    public void testInitialNonDreamingState() {
+        when(mDreamManager.isDreaming()).thenReturn(false);
+        final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
+        condition.addCallback(mCallback);
+
+        verify(mCallback, never()).onConditionChanged(eq(condition));
+        assertThat(condition.isConditionMet()).isFalse();
+    }
+
+    /**
      * Ensure that changing dream state triggers condition.
      */
     @Test
     public void testChange() {
-        final Intent intent = new Intent(Intent.ACTION_DREAMING_STARTED);
         final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
-        when(mContext.registerReceiver(receiverCaptor.capture(), any())).thenReturn(intent);
-        final DreamCondition condition = new DreamCondition(mContext);
+        when(mDreamManager.isDreaming()).thenReturn(true);
+        final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
         condition.addCallback(mCallback);
-        condition.start();
+        verify(mContext).registerReceiver(receiverCaptor.capture(), any());
         clearInvocations(mCallback);
         receiverCaptor.getValue().onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED));
         verify(mCallback).onConditionChanged(eq(condition));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
index de0e511..2db4596 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
@@ -20,6 +20,7 @@
 import android.test.suitebuilder.annotation.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -41,13 +42,14 @@
     @Mock lateinit var statusBarStateController: StatusBarStateController
     @Mock lateinit var powerManager: PowerManager
     val clock = FakeSystemClock()
+    val executor = FakeExecutor(clock)
     lateinit var listener: StatusBarStateController.StateListener
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
         restartDozeListener =
-            RestartDozeListener(settings, statusBarStateController, powerManager, clock)
+            RestartDozeListener(settings, statusBarStateController, powerManager, clock, executor)
 
         val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
         restartDozeListener.init()
@@ -58,12 +60,14 @@
     @Test
     fun testStoreDreamState_onDreamingStarted() {
         listener.onDreamingChanged(true)
+        executor.runAllReady()
         assertThat(settings.getBool(RestartDozeListener.RESTART_NAP_KEY)).isTrue()
     }
 
     @Test
     fun testStoreDreamState_onDreamingStopped() {
         listener.onDreamingChanged(false)
+        executor.runAllReady()
         assertThat(settings.getBool(RestartDozeListener.RESTART_NAP_KEY)).isFalse()
     }
 
@@ -71,6 +75,8 @@
     fun testRestoreDreamState_dreamingShouldStart() {
         settings.putBool(RestartDozeListener.RESTART_NAP_KEY, true)
         restartDozeListener.maybeRestartSleep()
+        executor.advanceClockToLast()
+        executor.runAllReady()
         verify(powerManager).wakeUp(clock.uptimeMillis())
         verify(powerManager).goToSleep(clock.uptimeMillis())
     }
@@ -79,6 +85,8 @@
     fun testRestoreDreamState_dreamingShouldNot() {
         settings.putBool(RestartDozeListener.RESTART_NAP_KEY, false)
         restartDozeListener.maybeRestartSleep()
+        executor.advanceClockToLast()
+        executor.runAllReady()
         verify(powerManager, never()).wakeUp(anyLong())
         verify(powerManager, never()).goToSleep(anyLong())
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
index 1365132..86246f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
@@ -39,8 +40,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -52,6 +55,7 @@
     private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
     private lateinit var deviceEntryFingerprintAuthRepository:
         FakeDeviceEntryFingerprintAuthRepository
+    @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var systemClock: SystemClock
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -73,6 +77,7 @@
         featureFlags = FakeFeatureFlags().apply { this.set(Flags.MODERN_ALTERNATE_BOUNCER, true) }
         underTest =
             AlternateBouncerInteractor(
+                statusBarStateController,
                 keyguardStateController,
                 bouncerRepository,
                 biometricSettingsRepository,
@@ -130,6 +135,14 @@
     }
 
     @Test
+    fun canShowAlternateBouncerForFingerprint_isDozing() {
+        givenCanShowAlternateBouncer()
+        whenever(statusBarStateController.isDozing).thenReturn(true)
+
+        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
+    }
+
+    @Test
     fun show_whenCanShow() {
         givenCanShowAlternateBouncer()
 
@@ -169,6 +182,42 @@
         assertFalse(bouncerRepository.alternateBouncerVisible.value)
     }
 
+    @Test
+    fun onUnlockedIsFalse_doesNotHide() {
+        // GIVEN alternate bouncer is showing
+        bouncerRepository.setAlternateVisible(true)
+
+        val keyguardStateControllerCallbackCaptor =
+            ArgumentCaptor.forClass(KeyguardStateController.Callback::class.java)
+        verify(keyguardStateController).addCallback(keyguardStateControllerCallbackCaptor.capture())
+
+        // WHEN isUnlocked=false
+        givenCanShowAlternateBouncer()
+        whenever(keyguardStateController.isUnlocked).thenReturn(false)
+        keyguardStateControllerCallbackCaptor.value.onUnlockedChanged()
+
+        // THEN the alternate bouncer is still visible
+        assertTrue(bouncerRepository.alternateBouncerVisible.value)
+    }
+
+    @Test
+    fun onUnlockedChangedIsTrue_hide() {
+        // GIVEN alternate bouncer is showing
+        bouncerRepository.setAlternateVisible(true)
+
+        val keyguardStateControllerCallbackCaptor =
+            ArgumentCaptor.forClass(KeyguardStateController.Callback::class.java)
+        verify(keyguardStateController).addCallback(keyguardStateControllerCallbackCaptor.capture())
+
+        // WHEN isUnlocked=true
+        givenCanShowAlternateBouncer()
+        whenever(keyguardStateController.isUnlocked).thenReturn(true)
+        keyguardStateControllerCallbackCaptor.value.onUnlockedChanged()
+
+        // THEN the alternate bouncer is hidden
+        assertFalse(bouncerRepository.alternateBouncerVisible.value)
+    }
+
     private fun givenCanShowAlternateBouncer() {
         bouncerRepository.setAlternateBouncerUIAvailable(true)
         biometricSettingsRepository.setFingerprintEnrolled(true)
@@ -176,6 +225,7 @@
         biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
         deviceEntryFingerprintAuthRepository.setLockedOut(false)
         whenever(keyguardStateController.isUnlocked).thenReturn(false)
+        whenever(statusBarStateController.isDozing).thenReturn(false)
     }
 
     private fun givenCannotShowAlternateBouncer() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
index 85e8d07..6c3d6f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo.Companion.DEFAULT_ICON_TINT
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -140,6 +141,7 @@
                 context.getString(R.string.media_transfer_receiver_content_description_unknown_app)
             )
         assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Resource(R.drawable.ic_cast))
+        assertThat(iconInfo.tint).isEqualTo(DEFAULT_ICON_TINT)
     }
 
     @Test
@@ -232,40 +234,40 @@
     fun iconInfo_toTintedIcon_loaded() {
         val contentDescription = ContentDescription.Loaded("test")
         val drawable = context.getDrawable(R.drawable.ic_cake)!!
-        val tintAttr = android.R.attr.textColorTertiary
+        val tint = R.color.GM2_blue_500
 
         val iconInfo =
             IconInfo(
                 contentDescription,
                 MediaTttIcon.Loaded(drawable),
-                tintAttr,
+                tint,
                 isAppIcon = false,
             )
 
         val tinted = iconInfo.toTintedIcon()
 
         assertThat(tinted.icon).isEqualTo(Icon.Loaded(drawable, contentDescription))
-        assertThat(tinted.tintAttr).isEqualTo(tintAttr)
+        assertThat(tinted.tint).isEqualTo(tint)
     }
 
     @Test
     fun iconInfo_toTintedIcon_resource() {
         val contentDescription = ContentDescription.Loaded("test")
         val drawableRes = R.drawable.ic_cake
-        val tintAttr = android.R.attr.textColorTertiary
+        val tint = R.color.GM2_blue_500
 
         val iconInfo =
             IconInfo(
                 contentDescription,
                 MediaTttIcon.Resource(drawableRes),
-                tintAttr,
+                tint,
                 isAppIcon = false
             )
 
         val tinted = iconInfo.toTintedIcon()
 
         assertThat(tinted.icon).isEqualTo(Icon.Resource(drawableRes, contentDescription))
-        assertThat(tinted.tintAttr).isEqualTo(tintAttr)
+        assertThat(tinted.tint).isEqualTo(tint)
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt
index 415e68f..bcc99bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt
@@ -73,6 +73,28 @@
         }
 
     @Test
+    fun isAnyShadeExpanded() =
+        testScope.runTest {
+            val underTest = create()
+            val isAnyShadeExpanded: Boolean? by collectLastValue(underTest.isAnyShadeExpanded)
+            assertWithMessage("isAnyShadeExpanded must start with false!")
+                .that(isAnyShadeExpanded)
+                .isFalse()
+
+            underTest.setExpansion(shadeId = ShadeId.LEFT, expansion = 0.441f)
+            assertThat(isAnyShadeExpanded).isTrue()
+
+            underTest.setExpansion(shadeId = ShadeId.RIGHT, expansion = 0.442f)
+            assertThat(isAnyShadeExpanded).isTrue()
+
+            underTest.setExpansion(shadeId = ShadeId.RIGHT, expansion = 0f)
+            assertThat(isAnyShadeExpanded).isTrue()
+
+            underTest.setExpansion(shadeId = ShadeId.LEFT, expansion = 0f)
+            assertThat(isAnyShadeExpanded).isFalse()
+        }
+
+    @Test
     fun isVisible_dualShadeConfig() =
         testScope.runTest {
             overrideResource(R.bool.dual_shade_enabled, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
new file mode 100644
index 0000000..f807146c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.multishade.domain.interactor
+
+import android.view.MotionEvent
+import android.view.ViewConfiguration
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
+import com.android.systemui.multishade.data.repository.MultiShadeRepository
+import com.android.systemui.multishade.shared.model.ProxiedInputModel
+import com.android.systemui.multishade.shared.model.ShadeId
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.currentTime
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
+
+    private lateinit var underTest: MultiShadeMotionEventInteractor
+
+    private lateinit var testScope: TestScope
+    private lateinit var motionEvents: MutableSet<MotionEvent>
+    private lateinit var repository: MultiShadeRepository
+    private lateinit var interactor: MultiShadeInteractor
+    private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
+
+    @Before
+    fun setUp() {
+        testScope = TestScope()
+        motionEvents = mutableSetOf()
+
+        val inputProxy = MultiShadeInputProxy()
+        repository =
+            MultiShadeRepository(
+                applicationContext = context,
+                inputProxy = inputProxy,
+            )
+        interactor =
+            MultiShadeInteractor(
+                applicationScope = testScope.backgroundScope,
+                repository = repository,
+                inputProxy = inputProxy,
+            )
+        underTest =
+            MultiShadeMotionEventInteractor(
+                applicationContext = context,
+                applicationScope = testScope.backgroundScope,
+                interactor = interactor,
+            )
+    }
+
+    @After
+    fun tearDown() {
+        motionEvents.forEach { motionEvent -> motionEvent.recycle() }
+    }
+
+    @Test
+    fun shouldIntercept_initialDown_returnsFalse() =
+        testScope.runTest {
+            assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))).isFalse()
+        }
+
+    @Test
+    fun shouldIntercept_moveBelowTouchSlop_returnsFalse() =
+        testScope.runTest {
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+            assertThat(
+                    underTest.shouldIntercept(
+                        motionEvent(
+                            MotionEvent.ACTION_MOVE,
+                            y = touchSlop - 1f,
+                        )
+                    )
+                )
+                .isFalse()
+        }
+
+    @Test
+    fun shouldIntercept_moveAboveTouchSlop_returnsTrue() =
+        testScope.runTest {
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+            assertThat(
+                    underTest.shouldIntercept(
+                        motionEvent(
+                            MotionEvent.ACTION_MOVE,
+                            y = touchSlop + 1f,
+                        )
+                    )
+                )
+                .isTrue()
+        }
+
+    @Test
+    fun shouldIntercept_moveAboveTouchSlop_butHorizontalFirst_returnsFalse() =
+        testScope.runTest {
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+            assertThat(
+                    underTest.shouldIntercept(
+                        motionEvent(
+                            MotionEvent.ACTION_MOVE,
+                            x = touchSlop + 1f,
+                        )
+                    )
+                )
+                .isFalse()
+            assertThat(
+                    underTest.shouldIntercept(
+                        motionEvent(
+                            MotionEvent.ACTION_MOVE,
+                            y = touchSlop + 1f,
+                        )
+                    )
+                )
+                .isFalse()
+        }
+
+    @Test
+    fun shouldIntercept_up_afterMovedAboveTouchSlop_returnsTrue() =
+        testScope.runTest {
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop + 1f))
+
+            assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))).isTrue()
+        }
+
+    @Test
+    fun shouldIntercept_cancel_afterMovedAboveTouchSlop_returnsTrue() =
+        testScope.runTest {
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop + 1f))
+
+            assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_CANCEL))).isTrue()
+        }
+
+    @Test
+    fun shouldIntercept_moveAboveTouchSlopAndUp_butShadeExpanded_returnsFalse() =
+        testScope.runTest {
+            repository.setExpansion(ShadeId.LEFT, 0.1f)
+            runCurrent()
+
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+            assertThat(
+                    underTest.shouldIntercept(
+                        motionEvent(
+                            MotionEvent.ACTION_MOVE,
+                            y = touchSlop + 1f,
+                        )
+                    )
+                )
+                .isFalse()
+            assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))).isFalse()
+        }
+
+    @Test
+    fun shouldIntercept_moveAboveTouchSlopAndCancel_butShadeExpanded_returnsFalse() =
+        testScope.runTest {
+            repository.setExpansion(ShadeId.LEFT, 0.1f)
+            runCurrent()
+
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+            assertThat(
+                    underTest.shouldIntercept(
+                        motionEvent(
+                            MotionEvent.ACTION_MOVE,
+                            y = touchSlop + 1f,
+                        )
+                    )
+                )
+                .isFalse()
+            assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_CANCEL))).isFalse()
+        }
+
+    @Test
+    fun tap_doesNotSendProxiedInput() =
+        testScope.runTest {
+            val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+            val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+            val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))
+
+            assertThat(leftShadeProxiedInput).isNull()
+            assertThat(rightShadeProxiedInput).isNull()
+            assertThat(singleShadeProxiedInput).isNull()
+        }
+
+    @Test
+    fun dragBelowTouchSlop_doesNotSendProxiedInput() =
+        testScope.runTest {
+            val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+            val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+            val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop - 1f))
+            underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))
+
+            assertThat(leftShadeProxiedInput).isNull()
+            assertThat(rightShadeProxiedInput).isNull()
+            assertThat(singleShadeProxiedInput).isNull()
+        }
+
+    @Test
+    fun dragAboveTouchSlopAndUp() =
+        testScope.runTest {
+            val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+            val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+            val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+            underTest.shouldIntercept(
+                motionEvent(
+                    MotionEvent.ACTION_DOWN,
+                    x = 100f, // left shade
+                )
+            )
+            assertThat(leftShadeProxiedInput).isNull()
+            assertThat(rightShadeProxiedInput).isNull()
+            assertThat(singleShadeProxiedInput).isNull()
+
+            val yDragAmountPx = touchSlop + 1f
+            val moveEvent =
+                motionEvent(
+                    MotionEvent.ACTION_MOVE,
+                    x = 100f, // left shade
+                    y = yDragAmountPx,
+                )
+            assertThat(underTest.shouldIntercept(moveEvent)).isTrue()
+            underTest.onTouchEvent(moveEvent, viewWidthPx = 1000)
+            assertThat(leftShadeProxiedInput)
+                .isEqualTo(
+                    ProxiedInputModel.OnDrag(
+                        xFraction = 0.1f,
+                        yDragAmountPx = yDragAmountPx,
+                    )
+                )
+            assertThat(rightShadeProxiedInput).isNull()
+            assertThat(singleShadeProxiedInput).isNull()
+
+            val upEvent = motionEvent(MotionEvent.ACTION_UP)
+            assertThat(underTest.shouldIntercept(upEvent)).isTrue()
+            underTest.onTouchEvent(upEvent, viewWidthPx = 1000)
+            assertThat(leftShadeProxiedInput).isNull()
+            assertThat(rightShadeProxiedInput).isNull()
+            assertThat(singleShadeProxiedInput).isNull()
+        }
+
+    @Test
+    fun dragAboveTouchSlopAndCancel() =
+        testScope.runTest {
+            val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+            val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+            val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+            underTest.shouldIntercept(
+                motionEvent(
+                    MotionEvent.ACTION_DOWN,
+                    x = 900f, // right shade
+                )
+            )
+            assertThat(leftShadeProxiedInput).isNull()
+            assertThat(rightShadeProxiedInput).isNull()
+            assertThat(singleShadeProxiedInput).isNull()
+
+            val yDragAmountPx = touchSlop + 1f
+            val moveEvent =
+                motionEvent(
+                    MotionEvent.ACTION_MOVE,
+                    x = 900f, // right shade
+                    y = yDragAmountPx,
+                )
+            assertThat(underTest.shouldIntercept(moveEvent)).isTrue()
+            underTest.onTouchEvent(moveEvent, viewWidthPx = 1000)
+            assertThat(leftShadeProxiedInput).isNull()
+            assertThat(rightShadeProxiedInput)
+                .isEqualTo(
+                    ProxiedInputModel.OnDrag(
+                        xFraction = 0.9f,
+                        yDragAmountPx = yDragAmountPx,
+                    )
+                )
+            assertThat(singleShadeProxiedInput).isNull()
+
+            val cancelEvent = motionEvent(MotionEvent.ACTION_CANCEL)
+            assertThat(underTest.shouldIntercept(cancelEvent)).isTrue()
+            underTest.onTouchEvent(cancelEvent, viewWidthPx = 1000)
+            assertThat(leftShadeProxiedInput).isNull()
+            assertThat(rightShadeProxiedInput).isNull()
+            assertThat(singleShadeProxiedInput).isNull()
+        }
+
+    private fun TestScope.motionEvent(
+        action: Int,
+        downTime: Long = currentTime,
+        eventTime: Long = currentTime,
+        x: Float = 0f,
+        y: Float = 0f,
+    ): MotionEvent {
+        val motionEvent = MotionEvent.obtain(downTime, eventTime, action, x, y, 0)
+        motionEvents.add(motionEvent)
+        return motionEvent
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt
new file mode 100644
index 0000000..8935309
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.multishade.shared.math
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class MathTest : SysuiTestCase() {
+
+    @Test
+    fun isZero_zero_true() {
+        assertThat(0f.isZero(epsilon = EPSILON)).isTrue()
+    }
+
+    @Test
+    fun isZero_belowPositiveEpsilon_true() {
+        assertThat((EPSILON * 0.999999f).isZero(epsilon = EPSILON)).isTrue()
+    }
+
+    @Test
+    fun isZero_aboveNegativeEpsilon_true() {
+        assertThat((EPSILON * -0.999999f).isZero(epsilon = EPSILON)).isTrue()
+    }
+
+    @Test
+    fun isZero_positiveEpsilon_false() {
+        assertThat(EPSILON.isZero(epsilon = EPSILON)).isFalse()
+    }
+
+    @Test
+    fun isZero_negativeEpsilon_false() {
+        assertThat((-EPSILON).isZero(epsilon = EPSILON)).isFalse()
+    }
+
+    @Test
+    fun isZero_positive_false() {
+        assertThat(1f.isZero(epsilon = EPSILON)).isFalse()
+    }
+
+    @Test
+    fun isZero_negative_false() {
+        assertThat((-1f).isZero(epsilon = EPSILON)).isFalse()
+    }
+
+    companion object {
+        private const val EPSILON = 0.0001f
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 0a8cd26..0ee52ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -46,6 +46,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
+import org.mockito.Mockito.isNull
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
@@ -267,7 +268,8 @@
 
         verifyZeroInteractions(context)
         val intentCaptor = argumentCaptor<Intent>()
-        verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle))
+        verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
+                isNull())
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
@@ -401,7 +403,8 @@
         createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
 
         val intentCaptor = argumentCaptor<Intent>()
-        verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle))
+        verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
+                isNull())
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
@@ -424,7 +427,8 @@
         createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
 
         val intentCaptor = argumentCaptor<Intent>()
-        verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle))
+        verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
+                isNull())
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
index 1f18d91..08b5d2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
@@ -19,12 +19,14 @@
 import android.content.ComponentName
 import android.content.pm.ActivityInfo
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
 import android.testing.AndroidTestingRunner
 import android.view.Display
 import android.view.IWindowManager
 import android.view.WindowManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import junit.framework.Assert.assertEquals
@@ -32,6 +34,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
@@ -158,4 +161,56 @@
         assertEquals(appName2, list[1])
         assertEquals(appName3, list[2])
     }
+
+    private fun includesFlagBits(@PackageManager.ComponentInfoFlagsBits mask: Int) =
+        ComponentInfoFlagMatcher(mask, mask)
+    private fun excludesFlagBits(@PackageManager.ComponentInfoFlagsBits mask: Int) =
+        ComponentInfoFlagMatcher(mask, 0)
+
+    private class ComponentInfoFlagMatcher(
+        @PackageManager.ComponentInfoFlagsBits val mask: Int, val value: Int
+    ): ArgumentMatcher<PackageManager.ComponentInfoFlags> {
+        override fun matches(flags: PackageManager.ComponentInfoFlags): Boolean {
+            return (mask.toLong() and flags.value) == value.toLong()
+        }
+
+        override fun toString(): String{
+            return "mask 0x%08x == 0x%08x".format(mask, value)
+        }
+    }
+
+    @Test
+    fun testMaybeNotifyOfScreenshot_disabledApp() {
+        val data = ScreenshotData.forTesting()
+        data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+
+        val component = ComponentName("package1", "class1")
+        val appName = "app name"
+        val activityInfo = mock(ActivityInfo::class.java)
+
+        whenever(
+            packageManager.getActivityInfo(
+                eq(component),
+                argThat(includesFlagBits(MATCH_DISABLED_COMPONENTS))
+            )
+        ).thenReturn(activityInfo);
+
+        whenever(
+            packageManager.getActivityInfo(
+                eq(component),
+                argThat(excludesFlagBits(MATCH_DISABLED_COMPONENTS))
+            )
+        ).thenThrow(PackageManager.NameNotFoundException::class.java);
+
+        whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
+            .thenReturn(listOf(component))
+
+        whenever(activityInfo.loadLabel(eq(packageManager))).thenReturn(appName)
+
+        val list = controller.maybeNotifyOfScreenshot(data)
+
+        assertEquals(1, list.size)
+        assertEquals(appName, list[0])
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 629208e..5f34b2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
 import com.android.systemui.multishade.data.repository.MultiShadeRepository
 import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
+import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.NotificationInsetsController
@@ -62,7 +63,6 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
-import org.mockito.Mockito
 import org.mockito.Mockito.anyFloat
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
@@ -129,6 +129,16 @@
 
         val inputProxy = MultiShadeInputProxy()
         testScope = TestScope()
+        val multiShadeInteractor =
+            MultiShadeInteractor(
+                applicationScope = testScope.backgroundScope,
+                repository =
+                    MultiShadeRepository(
+                        applicationContext = context,
+                        inputProxy = inputProxy,
+                    ),
+                inputProxy = inputProxy,
+            )
         underTest =
             NotificationShadeWindowViewController(
                 lockscreenShadeTransitionController,
@@ -154,18 +164,15 @@
                 keyguardTransitionInteractor,
                 primaryBouncerToGoneTransitionViewModel,
                 featureFlags,
+                { multiShadeInteractor },
+                FakeSystemClock(),
                 {
-                    MultiShadeInteractor(
+                    MultiShadeMotionEventInteractor(
+                        applicationContext = context,
                         applicationScope = testScope.backgroundScope,
-                        repository =
-                            MultiShadeRepository(
-                                applicationContext = context,
-                                inputProxy = inputProxy,
-                            ),
-                        inputProxy = inputProxy,
+                        interactor = multiShadeInteractor,
                     )
                 },
-                FakeSystemClock(),
             )
         underTest.setupExpandedStatusBar()
 
@@ -308,7 +315,7 @@
     fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() {
         // down event should be intercepted by keyguardViewManager
         whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(true)
+            .thenReturn(true)
 
         // Then touch should not be intercepted
         val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
@@ -316,14 +323,6 @@
     }
 
     @Test
-    fun testGetBouncerContainer() =
-        testScope.runTest {
-            Mockito.clearInvocations(view)
-            underTest.bouncerContainer
-            verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
-        }
-
-    @Test
     fun testGetKeyguardMessageArea() =
         testScope.runTest {
             underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index b4b5ec1..b40181e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
 import com.android.systemui.multishade.data.repository.MultiShadeRepository
 import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
+import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.statusbar.DragDownHelper
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -140,6 +141,16 @@
         featureFlags.set(Flags.DUAL_SHADE, false)
         val inputProxy = MultiShadeInputProxy()
         testScope = TestScope()
+        val multiShadeInteractor =
+            MultiShadeInteractor(
+                applicationScope = testScope.backgroundScope,
+                repository =
+                    MultiShadeRepository(
+                        applicationContext = context,
+                        inputProxy = inputProxy,
+                    ),
+                inputProxy = inputProxy,
+            )
         controller =
             NotificationShadeWindowViewController(
                 lockscreenShadeTransitionController,
@@ -165,18 +176,15 @@
                 keyguardTransitionInteractor,
                 primaryBouncerToGoneTransitionViewModel,
                 featureFlags,
+                { multiShadeInteractor },
+                FakeSystemClock(),
                 {
-                    MultiShadeInteractor(
+                    MultiShadeMotionEventInteractor(
+                        applicationContext = context,
                         applicationScope = testScope.backgroundScope,
-                        repository =
-                            MultiShadeRepository(
-                                applicationContext = context,
-                                inputProxy = inputProxy,
-                            ),
-                        inputProxy = inputProxy,
+                        interactor = multiShadeInteractor,
                     )
                 },
-                FakeSystemClock(),
             )
 
         controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/SplitShadeTransitionAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/SplitShadeTransitionAdapterTest.kt
new file mode 100644
index 0000000..64fec5b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/SplitShadeTransitionAdapterTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shade
+
+import android.animation.Animator
+import android.testing.AndroidTestingRunner
+import android.transition.TransitionValues
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardStatusViewController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shade.NotificationPanelViewController.SplitShadeTransitionAdapter
+import com.google.common.truth.Truth.assertThat
+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)
+class SplitShadeTransitionAdapterTest : SysuiTestCase() {
+
+    @Mock private lateinit var keyguardStatusViewController: KeyguardStatusViewController
+
+    private lateinit var adapter: SplitShadeTransitionAdapter
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        adapter = SplitShadeTransitionAdapter(keyguardStatusViewController)
+    }
+
+    @Test
+    fun createAnimator_nullStartValues_returnsNull() {
+        val animator = adapter.createAnimator(startValues = null, endValues = TransitionValues())
+
+        assertThat(animator).isNull()
+    }
+
+    @Test
+    fun createAnimator_nullEndValues_returnsNull() {
+        val animator = adapter.createAnimator(startValues = TransitionValues(), endValues = null)
+
+        assertThat(animator).isNull()
+    }
+
+    @Test
+    fun createAnimator_nonNullStartAndEndValues_returnsAnimator() {
+        val animator =
+            adapter.createAnimator(startValues = TransitionValues(), endValues = TransitionValues())
+
+        assertThat(animator).isNotNull()
+    }
+}
+
+private fun SplitShadeTransitionAdapter.createAnimator(
+    startValues: TransitionValues?,
+    endValues: TransitionValues?
+): Animator? {
+    return createAnimator(/* sceneRoot= */ null, startValues, endValues)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 653b0c7..09b00e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -858,6 +858,23 @@
     }
 
     @Test
+    public void testShouldNotScreen_appSuspended() throws RemoteException {
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+        when(mPowerManager.isInteractive()).thenReturn(false);
+        when(mStatusBarStateController.isDreaming()).thenReturn(false);
+        when(mStatusBarStateController.getState()).thenReturn(SHADE);
+        modifyRanking(entry).setSuspended(true).build();
+
+        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
+                .isEqualTo(FullScreenIntentDecision.NO_FSI_SUSPENDED);
+        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+                .isFalse();
+        verify(mLogger).logNoFullscreen(entry, "NO_FSI_SUSPENDED");
+        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+        verify(mLogger, never()).logFullscreen(any(), any());
+    }
+
+    @Test
     public void logFullScreenIntentDecision_shouldAlmostAlwaysLogOneTime() {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         Set<FullScreenIntentDecision> warnings = new HashSet<>(Arrays.asList(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
index 3108ed9..fe12051 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
@@ -21,6 +21,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.LayoutInflater;
+import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
@@ -49,6 +50,13 @@
     }
 
     @Test
+    public void userSwitcherChip_defaultVisibilityIsGone() {
+        assertThat(mKeyguardStatusBarView.findViewById(
+                R.id.user_switcher_container).getVisibility()).isEqualTo(
+                View.GONE);
+    }
+
+    @Test
     public void setTopClipping_clippingUpdated() {
         int topClipping = 40;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
index 3bc288a2..08e89fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
@@ -20,13 +20,17 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.StatusBarIcon
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl.EXTERNAL_SLOT_SUFFIX
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
+import org.mockito.Mock
 import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
 
 @SmallTest
 class StatusBarIconControllerImplTest : SysuiTestCase() {
@@ -34,15 +38,19 @@
     private lateinit var underTest: StatusBarIconControllerImpl
 
     private lateinit var iconList: StatusBarIconList
+    private lateinit var commandQueueCallbacks: CommandQueue.Callbacks
     private val iconGroup: StatusBarIconController.IconManager = mock()
 
+    @Mock private lateinit var commandQueue: CommandQueue
+
     @Before
     fun setUp() {
+        MockitoAnnotations.initMocks(this)
         iconList = StatusBarIconList(arrayOf())
         underTest =
             StatusBarIconControllerImpl(
                 context,
-                mock(),
+                commandQueue,
                 mock(),
                 mock(),
                 mock(),
@@ -51,11 +59,14 @@
                 mock(),
             )
         underTest.addIconGroup(iconGroup)
+        val commandQueueCallbacksCaptor = kotlinArgumentCaptor<CommandQueue.Callbacks>()
+        verify(commandQueue).addCallback(commandQueueCallbacksCaptor.capture())
+        commandQueueCallbacks = commandQueueCallbacksCaptor.value
     }
 
     /** Regression test for b/255428281. */
     @Test
-    fun internalAndExternalIconWithSameName_bothDisplayed() {
+    fun internalAndExternalIconWithSameName_externalFromTile_bothDisplayed() {
         val slotName = "mute"
 
         // Internal
@@ -71,7 +82,7 @@
                 /* number= */ 0,
                 "contentDescription",
             )
-        underTest.setIcon(slotName, externalIcon)
+        underTest.setIconFromTile(slotName, externalIcon)
 
         assertThat(iconList.slots).hasSize(2)
         // Whichever was added last comes first
@@ -83,17 +94,45 @@
 
     /** Regression test for b/255428281. */
     @Test
-    fun internalAndExternalIconWithSameName_externalRemoved_viaRemoveIcon_internalStays() {
+    fun internalAndExternalIconWithSameName_externalFromCommandQueue_bothDisplayed() {
         val slotName = "mute"
 
         // Internal
         underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
 
         // External
-        underTest.setIcon(slotName, createExternalIcon())
+        val externalIcon =
+            StatusBarIcon(
+                "external.package",
+                UserHandle.ALL,
+                /* iconId= */ 2,
+                /* iconLevel= */ 0,
+                /* number= */ 0,
+                "contentDescription",
+            )
+        commandQueueCallbacks.setIcon(slotName, externalIcon)
 
-        // WHEN the external icon is removed via #removeIcon
-        underTest.removeIcon(slotName)
+        assertThat(iconList.slots).hasSize(2)
+        // Whichever was added last comes first
+        assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+        assertThat(iconList.slots[1].name).isEqualTo(slotName)
+        assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
+        assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
+    }
+
+    /** Regression test for b/255428281. */
+    @Test
+    fun internalAndExternalIconWithSameName_externalRemoved_fromCommandQueue_internalStays() {
+        val slotName = "mute"
+
+        // Internal
+        underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+        // External
+        commandQueueCallbacks.setIcon(slotName, createExternalIcon())
+
+        // WHEN the external icon is removed via CommandQueue.Callbacks#removeIcon
+        commandQueueCallbacks.removeIcon(slotName)
 
         // THEN the external icon is removed but the internal icon remains
         // Note: [StatusBarIconList] never removes slots from its list, it just sets the holder for
@@ -109,17 +148,17 @@
 
     /** Regression test for b/255428281. */
     @Test
-    fun internalAndExternalIconWithSameName_externalRemoved_viaRemoveAll_internalStays() {
+    fun internalAndExternalIconWithSameName_externalRemoved_fromTileRemove_internalStays() {
         val slotName = "mute"
 
         // Internal
         underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
 
         // External
-        underTest.setIcon(slotName, createExternalIcon())
+        underTest.setIconFromTile(slotName, createExternalIcon())
 
-        // WHEN the external icon is removed via #removeAllIconsForExternalSlot
-        underTest.removeAllIconsForExternalSlot(slotName)
+        // WHEN the external icon is removed via #removeIconForTile
+        underTest.removeIconForTile(slotName)
 
         // THEN the external icon is removed but the internal icon remains
         assertThat(iconList.slots).hasSize(2)
@@ -133,17 +172,17 @@
 
     /** Regression test for b/255428281. */
     @Test
-    fun internalAndExternalIconWithSameName_externalRemoved_viaSetNull_internalStays() {
+    fun internalAndExternalIconWithSameName_externalRemoved_fromTileSetNull_internalStays() {
         val slotName = "mute"
 
         // Internal
         underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
 
         // External
-        underTest.setIcon(slotName, createExternalIcon())
+        underTest.setIconFromTile(slotName, createExternalIcon())
 
-        // WHEN the external icon is removed via a #setIcon(null)
-        underTest.setIcon(slotName, /* icon= */ null)
+        // WHEN the external icon is removed via a #setIconFromTile(null)
+        underTest.setIconFromTile(slotName, /* icon= */ null)
 
         // THEN the external icon is removed but the internal icon remains
         assertThat(iconList.slots).hasSize(2)
@@ -164,12 +203,12 @@
         underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
 
         // External
-        underTest.setIcon(slotName, createExternalIcon())
+        underTest.setIconFromTile(slotName, createExternalIcon())
 
         // WHEN the internal icon is removed via #removeIcon
         underTest.removeIcon(slotName, /* tag= */ 0)
 
-        // THEN the external icon is removed but the internal icon remains
+        // THEN the internal icon is removed but the external icon remains
         assertThat(iconList.slots).hasSize(2)
         assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
         assertThat(iconList.slots[1].name).isEqualTo(slotName)
@@ -188,12 +227,12 @@
         underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
 
         // External
-        underTest.setIcon(slotName, createExternalIcon())
+        underTest.setIconFromTile(slotName, createExternalIcon())
 
         // WHEN the internal icon is removed via #removeAllIconsForSlot
         underTest.removeAllIconsForSlot(slotName)
 
-        // THEN the external icon is removed but the internal icon remains
+        // THEN the internal icon is removed but the external icon remains
         assertThat(iconList.slots).hasSize(2)
         assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
         assertThat(iconList.slots[1].name).isEqualTo(slotName)
@@ -221,7 +260,7 @@
                 /* number= */ 0,
                 "externalDescription",
             )
-        underTest.setIcon(slotName, startingExternalIcon)
+        underTest.setIconFromTile(slotName, startingExternalIcon)
 
         // WHEN the internal icon is updated
         underTest.setIcon(slotName, /* resourceId= */ 11, "newContentDescription")
@@ -243,7 +282,7 @@
 
     /** Regression test for b/255428281. */
     @Test
-    fun internalAndExternalIconWithSameName_externalUpdatedIndependently() {
+    fun internalAndExternalIconWithSameName_fromTile_externalUpdatedIndependently() {
         val slotName = "mute"
 
         // Internal
@@ -259,7 +298,7 @@
                 /* number= */ 0,
                 "externalDescription",
             )
-        underTest.setIcon(slotName, startingExternalIcon)
+        underTest.setIconFromTile(slotName, startingExternalIcon)
 
         // WHEN the external icon is updated
         val newExternalIcon =
@@ -271,7 +310,54 @@
                 /* number= */ 0,
                 "newExternalDescription",
             )
-        underTest.setIcon(slotName, newExternalIcon)
+        underTest.setIconFromTile(slotName, newExternalIcon)
+
+        // THEN only the external slot gets the updates
+        val externalSlot = iconList.slots[0]
+        val externalHolder = externalSlot.getHolderForTag(TAG_PRIMARY)!!
+        assertThat(externalSlot.name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+        assertThat(externalHolder.icon!!.contentDescription).isEqualTo("newExternalDescription")
+        assertThat(externalHolder.icon!!.icon.resId).isEqualTo(21)
+
+        // And the internal slot has its own values
+        val internalSlot = iconList.slots[1]
+        val internalHolder = internalSlot.getHolderForTag(TAG_PRIMARY)!!
+        assertThat(internalSlot.name).isEqualTo(slotName)
+        assertThat(internalHolder.icon!!.contentDescription).isEqualTo("contentDescription")
+        assertThat(internalHolder.icon!!.icon.resId).isEqualTo(10)
+    }
+
+    /** Regression test for b/255428281. */
+    @Test
+    fun internalAndExternalIconWithSameName_fromCommandQueue_externalUpdatedIndependently() {
+        val slotName = "mute"
+
+        // Internal
+        underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+        // External
+        val startingExternalIcon =
+            StatusBarIcon(
+                "external.package",
+                UserHandle.ALL,
+                /* iconId= */ 20,
+                /* iconLevel= */ 0,
+                /* number= */ 0,
+                "externalDescription",
+            )
+        commandQueueCallbacks.setIcon(slotName, startingExternalIcon)
+
+        // WHEN the external icon is updated
+        val newExternalIcon =
+            StatusBarIcon(
+                "external.package",
+                UserHandle.ALL,
+                /* iconId= */ 21,
+                /* iconLevel= */ 0,
+                /* number= */ 0,
+                "newExternalDescription",
+            )
+        commandQueueCallbacks.setIcon(slotName, newExternalIcon)
 
         // THEN only the external slot gets the updates
         val externalSlot = iconList.slots[0]
@@ -289,8 +375,16 @@
     }
 
     @Test
-    fun externalSlot_alreadyEndsWithSuffix_suffixNotAddedTwice() {
-        underTest.setIcon("myslot$EXTERNAL_SLOT_SUFFIX", createExternalIcon())
+    fun externalSlot_fromTile_alreadyEndsWithSuffix_suffixNotAddedTwice() {
+        underTest.setIconFromTile("myslot$EXTERNAL_SLOT_SUFFIX", createExternalIcon())
+
+        assertThat(iconList.slots).hasSize(1)
+        assertThat(iconList.slots[0].name).isEqualTo("myslot$EXTERNAL_SLOT_SUFFIX")
+    }
+
+    @Test
+    fun externalSlot_fromCommandQueue_alreadyEndsWithSuffix_suffixNotAddedTwice() {
+        commandQueueCallbacks.setIcon("myslot$EXTERNAL_SLOT_SUFFIX", createExternalIcon())
 
         assertThat(iconList.slots).hasSize(1)
         assertThat(iconList.slots[0].name).isEqualTo("myslot$EXTERNAL_SLOT_SUFFIX")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index d9546877..14aee4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -154,7 +154,6 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
         when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
         when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
                 .thenReturn(mKeyguardMessageAreaController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 64545b1..be0c83f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -294,6 +294,13 @@
     }
 
     @Test
+    public void userChip_defaultVisibilityIsGone() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        assertEquals(View.GONE, getUserChipView().getVisibility());
+    }
+
+    @Test
     public void disable_noOngoingCall_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -333,6 +340,19 @@
     }
 
     @Test
+    public void disable_hasOngoingCallButAlsoHun_chipHidden() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+    }
+
+    @Test
     public void disable_ongoingCallEnded_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -558,6 +578,10 @@
         return (CollapsedStatusBarFragment) mFragment;
     }
 
+    private View getUserChipView() {
+        return mFragment.getView().findViewById(R.id.user_switcher_container);
+    }
+
     private View getClockView() {
         return mFragment.getView().findViewById(R.id.clock);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
index 4525ad2..17f8ec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
@@ -507,11 +507,29 @@
         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
 
         verify(uiEventLogger, times(1))
-            .logWithInstanceId(
+            .logWithInstanceIdAndPosition(
                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
                 0,
                 null,
-                InstanceId.fakeInstanceId(instanceIdSequenceFake.lastInstanceId)
+                InstanceId.fakeInstanceId(instanceIdSequenceFake.lastInstanceId),
+                0,
+            )
+    }
+
+    @Test
+    fun onBatteryStateChanged_batteryPresent_btStylusPresent_logsSessionStart() {
+        whenever(batteryState.isPresent).thenReturn(true)
+        stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
+
+        stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
+
+        verify(uiEventLogger, times(1))
+            .logWithInstanceIdAndPosition(
+                StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
+                0,
+                null,
+                InstanceId.fakeInstanceId(instanceIdSequenceFake.lastInstanceId),
+                1,
             )
     }
 
@@ -545,19 +563,21 @@
         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
 
         verify(uiEventLogger, times(1))
-            .logWithInstanceId(
+            .logWithInstanceIdAndPosition(
                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
                 0,
                 null,
-                instanceId
+                instanceId,
+                0
             )
 
         verify(uiEventLogger, times(1))
-            .logWithInstanceId(
+            .logWithInstanceIdAndPosition(
                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED,
                 0,
                 null,
-                instanceId
+                instanceId,
+                0
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 586bdc6..6e24941 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -685,7 +685,7 @@
         allowSwipeToDismiss: Boolean = false,
     ): ChipbarInfo {
         return ChipbarInfo(
-            TintedIcon(startIcon, tintAttr = null),
+            TintedIcon(startIcon, tint = null),
             text,
             endItem,
             vibrationEffect,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
index 075f393..84129be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
@@ -16,10 +16,16 @@
 
 package com.android.systemui.util.sensors;
 
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import android.content.res.Resources;
+import android.hardware.Sensor;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -46,28 +52,52 @@
     @Mock private Resources mResources;
     @Mock private DevicePostureController mDevicePostureController;
     @Mock private AsyncSensorManager mSensorManager;
+    @Mock private Sensor mMockedPrimaryProxSensor;
 
     @Captor private ArgumentCaptor<DevicePostureController.Callback> mPostureListenerCaptor =
             ArgumentCaptor.forClass(DevicePostureController.Callback.class);
     private DevicePostureController.Callback mPostureListener;
 
-    private PostureDependentProximitySensor mProximitySensor;
-    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+    private PostureDependentProximitySensor mPostureDependentProximitySensor;
+    private ThresholdSensor[] mPrimaryProxSensors;
+    private ThresholdSensor[] mSecondaryProxSensors;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         allowTestableLooperAsMainThread();
 
-        mProximitySensor = new PostureDependentProximitySensor(
-                new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE],
-                new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE],
-                mFakeExecutor,
+        setupProximitySensors(DEVICE_POSTURE_CLOSED);
+        mPostureDependentProximitySensor = new PostureDependentProximitySensor(
+                mPrimaryProxSensors,
+                mSecondaryProxSensors,
+                new FakeExecutor(new FakeSystemClock()),
                 new FakeExecution(),
                 mDevicePostureController
         );
     }
 
+    /**
+     * Support a proximity sensor only for the given devicePosture for the primary sensor.
+     * Otherwise, all other postures don't support prox.
+     */
+    private void setupProximitySensors(
+            @DevicePostureController.DevicePostureInt int proxExistsForPosture) {
+        final ThresholdSensorImpl.Builder sensorBuilder = new ThresholdSensorImpl.BuilderFactory(
+                mResources, mSensorManager, new FakeExecution()).createBuilder();
+
+        mPrimaryProxSensors = new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE];
+        mSecondaryProxSensors =
+                new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE];
+        for (int i = 0; i < DevicePostureController.SUPPORTED_POSTURES_SIZE; i++) {
+            mPrimaryProxSensors[i] = sensorBuilder.setSensor(null).setThresholdValue(0).build();
+            mSecondaryProxSensors[i] = sensorBuilder.setSensor(null).setThresholdValue(0).build();
+        }
+
+        mPrimaryProxSensors[proxExistsForPosture] = sensorBuilder
+                .setSensor(mMockedPrimaryProxSensor).setThresholdValue(5).build();
+    }
+
     @Test
     public void testPostureChangeListenerAdded() {
         capturePostureListener();
@@ -83,30 +113,59 @@
 
         // THEN device posture is updated to DEVICE_POSTURE_OPENED
         assertEquals(DevicePostureController.DEVICE_POSTURE_OPENED,
-                mProximitySensor.mDevicePosture);
+                mPostureDependentProximitySensor.mDevicePosture);
 
         // WHEN the posture changes to DEVICE_POSTURE_CLOSED
-        mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_CLOSED);
+        mPostureListener.onPostureChanged(DEVICE_POSTURE_CLOSED);
 
         // THEN device posture is updated to DEVICE_POSTURE_CLOSED
-        assertEquals(DevicePostureController.DEVICE_POSTURE_CLOSED,
-                mProximitySensor.mDevicePosture);
+        assertEquals(DEVICE_POSTURE_CLOSED,
+                mPostureDependentProximitySensor.mDevicePosture);
 
         // WHEN the posture changes to DEVICE_POSTURE_FLIPPED
         mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_FLIPPED);
 
         // THEN device posture is updated to DEVICE_POSTURE_FLIPPED
         assertEquals(DevicePostureController.DEVICE_POSTURE_FLIPPED,
-                mProximitySensor.mDevicePosture);
+                mPostureDependentProximitySensor.mDevicePosture);
 
         // WHEN the posture changes to DEVICE_POSTURE_HALF_OPENED
         mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
 
         // THEN device posture is updated to DEVICE_POSTURE_HALF_OPENED
         assertEquals(DevicePostureController.DEVICE_POSTURE_HALF_OPENED,
-                mProximitySensor.mDevicePosture);
+                mPostureDependentProximitySensor.mDevicePosture);
     }
 
+    @Test
+    public void proxSensorRegisters_proxSensorValid() {
+        // GIVEN posture that supports a valid posture with a prox sensor
+        capturePostureListener();
+        mPostureListener.onPostureChanged(DEVICE_POSTURE_CLOSED);
+
+        // WHEN a listener registers
+        mPostureDependentProximitySensor.register(mock(ThresholdSensor.Listener.class));
+
+        // THEN PostureDependentProximitySensor is registered
+        assertTrue(mPostureDependentProximitySensor.isRegistered());
+    }
+
+    @Test
+    public void proxSensorReregisters_postureChangesAndNewlySupportsProx() {
+        // GIVEN there's a registered listener but posture doesn't support prox
+        assertFalse(mPostureDependentProximitySensor.isRegistered());
+        mPostureDependentProximitySensor.register(mock(ThresholdSensor.Listener.class));
+        assertFalse(mPostureDependentProximitySensor.isRegistered());
+
+        // WHEN posture that supports a valid posture with a prox sensor
+        capturePostureListener();
+        mPostureListener.onPostureChanged(DEVICE_POSTURE_CLOSED);
+
+        // THEN PostureDependentProximitySensor is registered
+        assertTrue(mPostureDependentProximitySensor.isRegistered());
+    }
+
+
     private void capturePostureListener() {
         verify(mDevicePostureController).addCallback(mPostureListenerCaptor.capture());
         mPostureListener = mPostureListenerCaptor.getValue();
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 ee4e00b..cc3b4ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -284,6 +284,8 @@
     private ShadeWindowLogger mShadeWindowLogger;
     @Mock
     private NotifPipelineFlags mNotifPipelineFlags;
+    @Mock
+    private Icon mAppBubbleIcon;
 
     private TestableBubblePositioner mPositioner;
 
@@ -303,7 +305,7 @@
         // For the purposes of this test, just run everything synchronously
         ShellExecutor syncExecutor = new SyncExecutor();
 
-        mUser0 = createUserHande(/* userId= */ 0);
+        mUser0 = createUserHandle(/* userId= */ 0);
 
         when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
         when(mNotificationShadeWindowView.getViewTreeObserver())
@@ -1250,7 +1252,7 @@
 
     @Test
     public void testShowManageMenuChangesSysuiState_appBubble() {
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
         assertTrue(mBubbleController.hasBubbles());
 
         // Expand the stack
@@ -1671,7 +1673,7 @@
         assertThat(mBubbleController.isStackExpanded()).isFalse();
         assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();
 
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
 
         verify(mBubbleController).inflateAndAdd(any(Bubble.class), /* suppressFlyout= */ eq(true),
                 /* showInShade= */ eq(false));
@@ -1681,13 +1683,13 @@
 
     @Test
     public void testShowOrHideAppBubble_expandIfCollapsed() {
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
         mBubbleController.updateBubble(mBubbleEntry);
         mBubbleController.collapseStack();
         assertThat(mBubbleController.isStackExpanded()).isFalse();
 
         // Calling this while collapsed will expand the app bubble
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
 
         assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
         assertThat(mBubbleController.isStackExpanded()).isTrue();
@@ -1696,12 +1698,12 @@
 
     @Test
     public void testShowOrHideAppBubble_collapseIfSelected() {
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
         assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
         assertThat(mBubbleController.isStackExpanded()).isTrue();
 
         // Calling this while the app bubble is expanded should collapse the stack
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
 
         assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
         assertThat(mBubbleController.isStackExpanded()).isFalse();
@@ -1711,15 +1713,15 @@
 
     @Test
     public void testShowOrHideAppBubbleWithNonPrimaryUser_bubbleCollapsedWithExpectedUser() {
-        UserHandle user10 = createUserHande(/* userId = */ 10);
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10);
+        UserHandle user10 = createUserHandle(/* userId = */ 10);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
         assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
         assertThat(mBubbleController.isStackExpanded()).isTrue();
         assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
         assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(user10);
 
         // Calling this while the app bubble is expanded should collapse the stack
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
 
         assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
         assertThat(mBubbleController.isStackExpanded()).isFalse();
@@ -1729,13 +1731,13 @@
 
     @Test
     public void testShowOrHideAppBubble_selectIfNotSelected() {
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
         mBubbleController.updateBubble(mBubbleEntry);
         mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
         assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(mBubbleEntry.getKey());
         assertThat(mBubbleController.isStackExpanded()).isTrue();
 
-        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
         assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
         assertThat(mBubbleController.isStackExpanded()).isTrue();
         assertThat(mBubbleData.getBubbles().size()).isEqualTo(2);
@@ -1870,7 +1872,7 @@
         mBubbleController.onUserChanged(userId);
     }
 
-    private UserHandle createUserHande(int userId) {
+    private UserHandle createUserHandle(int userId) {
         UserHandle user = mock(UserHandle.class);
         when(user.getIdentifier()).thenReturn(userId);
         return user;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 926c6c5..c664c99 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -47,7 +47,12 @@
     }
 
     @Override
-    public void setExternalIcon(String slot) {
+    public void setIconFromTile(String slot, StatusBarIcon icon) {
+
+    }
+
+    @Override
+    public void removeIconForTile(String slot) {
 
     }
 
@@ -57,11 +62,6 @@
     }
 
     @Override
-    public void setIcon(String slot, StatusBarIcon icon) {
-
-    }
-
-    @Override
     public void setWifiIcon(String slot, WifiIconState state) {
     }
 
@@ -98,10 +98,6 @@
     }
 
     @Override
-    public void removeAllIconsForExternalSlot(String slot) {
-    }
-
-    @Override
     public void setIconAccessibilityLiveRegion(String slot, int mode) {
     }
 
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index ee806a6..1298f63 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -2076,6 +2076,7 @@
         ret.outputId.id = output.getId();
         ret.physicalCameraId = output.getPhysicalCameraId();
         ret.surfaceGroupId = output.getSurfaceGroupId();
+        ret.isMultiResolutionOutput = false;
         if (output instanceof SurfaceOutputConfigImpl) {
             SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output;
             ret.type = CameraOutputConfig.TYPE_SURFACE;
@@ -2095,6 +2096,7 @@
             ret.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER;
             ret.imageFormat = multiResReaderConfig.getImageFormat();
             ret.capacity = multiResReaderConfig.getMaxImages();
+            ret.isMultiResolutionOutput = true;
         } else {
             throw new IllegalStateException("Unknown output config type!");
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 01386da..af5b196 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -383,7 +383,7 @@
             final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
             if (service == null) {
                 // If we cannot get the service from the services cache, it will call
-                // updateRemoteAugmentedAutofillService() finally. Skip call this update again.
+                // updateRemoteFieldClassificationService() finally. Skip call this update again.
                 getServiceForUserLocked(userId);
             } else {
                 service.updateRemoteFieldClassificationService();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index bea049c..d5dcdaf 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1731,7 +1731,7 @@
     }
 
     /**
-     * Called when the {@link AutofillManagerService#mAugmentedAutofillResolver}
+     * Called when the {@link AutofillManagerService#mFieldClassificationResolver}
      * changed (among other places).
      */
     void updateRemoteFieldClassificationService() {
@@ -1742,7 +1742,6 @@
                             + "destroying old remote service");
                 }
                 mRemoteFieldClassificationService.unbind();
-
                 mRemoteFieldClassificationService = null;
                 mRemoteFieldClassificationServiceInfo = null;
             }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index b4e636e..62a2970 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -327,13 +327,11 @@
     private int setTemporaryDetectionService(PrintWriter pw) {
         final int userId = getNextIntArgRequired();
         final String serviceName = getNextArg();
-        final int duration = getNextIntArgRequired();
-
         if (serviceName == null) {
             mService.resetTemporaryDetectionService(userId);
             return 0;
         }
-
+        final int duration = getNextIntArgRequired();
         if (duration <= 0) {
             mService.resetTemporaryDetectionService(userId);
             return 0;
diff --git a/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java b/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java
new file mode 100644
index 0000000..3b30af6
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_EXPLICITLY_REQUESTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_NORMAL_TRIGGER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_PRE_TRIGGER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_RETRIGGER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.IntDef;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Optional;
+
+/**
+ * Helper class to log Autofill FillRequest filing stats.
+ */
+public final class FillRequestEventLogger {
+    private static final String TAG = "FillRequestEventLogger";
+
+    /**
+     * Reasons why presentation was not shown. These are wrappers around
+     * {@link com.android.os.AtomsProto.AutofillFillRequestReported.RequestTriggerReason}.
+     */
+    @IntDef(prefix = {"TRIGGER_REASON"}, value = {
+            TRIGGER_REASON_UNKNOWN,
+            TRIGGER_REASON_EXPLICITLY_REQUESTED,
+            TRIGGER_REASON_RETRIGGER,
+            TRIGGER_REASON_PRE_TRIGGER,
+            TRIGGER_REASON_NORMAL_TRIGGER,
+            TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TriggerReason {
+    }
+
+    public static final int TRIGGER_REASON_UNKNOWN =
+            AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
+    public static final int TRIGGER_REASON_EXPLICITLY_REQUESTED =
+            AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_EXPLICITLY_REQUESTED;
+    public static final int TRIGGER_REASON_RETRIGGER =
+            AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_RETRIGGER;
+    public static final int TRIGGER_REASON_PRE_TRIGGER =
+            AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_PRE_TRIGGER;
+    public static final int TRIGGER_REASON_NORMAL_TRIGGER =
+            AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_NORMAL_TRIGGER;
+    public static final int TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE =
+            AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
+
+    private final int mSessionId;
+    private Optional<FillRequestEventInternal> mEventInternal;
+
+    private FillRequestEventLogger(int sessionId) {
+        mSessionId = sessionId;
+        mEventInternal = Optional.empty();
+    }
+
+    /**
+     * A factory constructor to create FillRequestEventLogger.
+     */
+    public static FillRequestEventLogger forSessionId(int sessionId) {
+        return new FillRequestEventLogger(sessionId);
+    }
+    /**
+     * Reset mEventInternal before logging for a new request. It shall be called for each
+     * FillRequest.
+     */
+    public void startLogForNewRequest() {
+        if (!mEventInternal.isEmpty()) {
+            Slog.w(TAG, "FillRequestEventLogger is not empty before starting for a new " +
+                    "request");
+        }
+        mEventInternal = Optional.of(new FillRequestEventInternal());
+    }
+
+    /**
+     * Set request_id as long as mEventInternal presents.
+     */
+    public void maybeSetRequestId(int requestId) {
+        mEventInternal.ifPresent(event -> event.mRequestId = requestId);
+    }
+
+    /**
+     * Set service_uid as long as mEventInternal presents.
+     */
+    public void maybeSetAutofillServiceUid(int uid) {
+        mEventInternal.ifPresent(event -> {
+            event.mAutofillServiceUid = uid;
+        });
+    }
+
+    /**
+     * Set inline_suggestion_host_uid as long as mEventInternal presents.
+     */
+    public void maybeSetInlineSuggestionHostUid(Context context, int userId) {
+        mEventInternal.ifPresent(event -> {
+            String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
+                    Settings.Secure.DEFAULT_INPUT_METHOD, userId);
+            if (TextUtils.isEmpty(imeString)) {
+                Slog.w(TAG, "No default IME found");
+                return;
+            }
+            ComponentName imeComponent = ComponentName.unflattenFromString(imeString);
+            if (imeComponent == null) {
+                Slog.w(TAG, "No default IME found");
+                return;
+            }
+            int imeUid;
+            String packageName = imeComponent.getPackageName();
+            try {
+                imeUid = context.getPackageManager().getApplicationInfoAsUser(packageName,
+                        PackageManager.ApplicationInfoFlags.of(0), userId).uid;
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.w(TAG, "Couldn't find packageName: " + packageName);
+                return;
+            }
+            event.mInlineSuggestionHostUid = imeUid;
+        });
+    }
+
+
+    /**
+     * Set flags as long as mEventInternal presents.
+     */
+    public void maybeSetFlags(int flags) {
+        mEventInternal.ifPresent(event -> {
+            event.mFlags = flags;
+        });
+    }
+
+    /**
+     * Set request_trigger_reason as long as mEventInternal presents.
+     */
+    public void maybeSetRequestTriggerReason(@TriggerReason int reason) {
+        mEventInternal.ifPresent(event -> {
+            event.mRequestTriggerReason = reason;
+        });
+    }
+
+    /**
+     * Set is_augmented as long as mEventInternal presents.
+     */
+    public void maybeSetIsAugmented(boolean val) {
+        mEventInternal.ifPresent(event -> {
+            event.mIsAugmented = val;
+        });
+    }
+
+    /**
+     * Set is_client_suggestion as long as mEventInternal presents.
+     */
+    public void maybeSetIsClientSuggestionFallback(boolean val) {
+        mEventInternal.ifPresent(event -> {
+            event.mIsClientSuggestionFallback = val;
+        });
+    }
+
+    /**
+     * Set is_fill_dialog_eligible as long as mEventInternal presents.
+     */
+    public void maybeSetIsFillDialogEligible(boolean val) {
+        mEventInternal.ifPresent(event -> {
+            event.mIsFillDialogEligible = val;
+        });
+    }
+
+    /**
+     * Set latency_fill_request_sent_millis as long as mEventInternal presents.
+     */
+    public void maybeSetLatencyFillRequestSentMillis(int timestamp) {
+        mEventInternal.ifPresent(event -> {
+            event.mLatencyFillRequestSentMillis = timestamp;
+        });
+    }
+
+    /**
+     * Set app_package_uid as long as mEventInternal presents.
+     */
+    public void maybeSetAppPackageUid(int uid) {
+        mEventInternal.ifPresent(event -> {
+            event.mAppPackageUid = uid;
+        });
+    }
+
+    /**
+     * Log an AUTOFILL_FILL_REQUEST_REPORTED event.
+     */
+    public void logAndEndEvent() {
+        if (!mEventInternal.isPresent()) {
+            Slog.w(TAG, "Shouldn't be logging AutofillFillRequestReported again for same "
+                    + "event");
+            return;
+        }
+        FillRequestEventInternal event = mEventInternal.get();
+        if (sVerbose) {
+            Slog.v(TAG, "Log AutofillFillRequestReported:"
+                    + " requestId=" + event.mRequestId
+                    + " sessionId=" + mSessionId
+                    + " mAutofillServiceUid=" + event.mAutofillServiceUid
+                    + " mInlineSuggestionHostUid=" + event.mInlineSuggestionHostUid
+                    + " mIsAugmented=" + event.mIsAugmented
+                    + " mIsClientSuggestionFallback=" + event.mIsClientSuggestionFallback
+                    + " mIsFillDialogEligible=" + event.mIsFillDialogEligible
+                    + " mRequestTriggerReason=" + event.mRequestTriggerReason
+                    + " mFlags=" + event.mFlags
+                    + " mLatencyFillRequestSentMillis=" + event.mLatencyFillRequestSentMillis
+                    + " mAppPackageUid=" + event.mAppPackageUid);
+        }
+        FrameworkStatsLog.write(
+                AUTOFILL_FILL_REQUEST_REPORTED,
+                event.mRequestId,
+                mSessionId,
+                event.mAutofillServiceUid,
+                event.mInlineSuggestionHostUid,
+                event.mIsAugmented,
+                event.mIsClientSuggestionFallback,
+                event.mIsFillDialogEligible,
+                event.mRequestTriggerReason,
+                event.mFlags,
+                event.mLatencyFillRequestSentMillis,
+                event.mAppPackageUid);
+        mEventInternal = Optional.empty();
+    }
+
+    private static final class FillRequestEventInternal {
+        int mRequestId;
+        int mAppPackageUid = -1;
+        int mAutofillServiceUid = -1;
+        int mInlineSuggestionHostUid = -1;
+        boolean mIsAugmented = false;
+        boolean mIsClientSuggestionFallback = false;
+        boolean mIsFillDialogEligible = false;
+        int mRequestTriggerReason =
+                AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
+        int mFlags = -1;
+        int mLatencyFillRequestSentMillis = -1;
+
+        FillRequestEventInternal() {
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 590f472..7d1de40 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -38,6 +38,7 @@
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
 import static com.android.server.autofill.Helper.sVerbose;
 
@@ -71,6 +72,7 @@
     @IntDef(prefix = {"NOT_SHOWN_REASON"}, value = {
             NOT_SHOWN_REASON_ANY_SHOWN,
             NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED,
+            NOT_SHOWN_REASON_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE,
             NOT_SHOWN_REASON_VIEW_CHANGED,
             NOT_SHOWN_REASON_ACTIVITY_FINISHED,
             NOT_SHOWN_REASON_REQUEST_TIMEOUT,
@@ -86,6 +88,8 @@
             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
     public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
+    public static final int NOT_SHOWN_REASON_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE;
     public static final int NOT_SHOWN_REASON_VIEW_CHANGED =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
     public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED =
@@ -142,7 +146,7 @@
     }
 
     public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
-            AutofillId currentViewId) {
+                                       AutofillId currentViewId) {
         mEventInternal.ifPresent(event -> {
             int availableCount = getDatasetCountForAutofillId(datasetList, currentViewId);
             event.mAvailableCount = availableCount;
@@ -151,7 +155,7 @@
     }
 
     public void maybeSetCountShown(@Nullable List<Dataset> datasetList,
-            AutofillId currentViewId) {
+                                   AutofillId currentViewId) {
         mEventInternal.ifPresent(event -> {
             int countShown = getDatasetCountForAutofillId(datasetList, currentViewId);
             event.mCountShown = countShown;
@@ -162,7 +166,7 @@
     }
 
     private static int getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
-            AutofillId currentViewId) {
+                                                    AutofillId currentViewId) {
         int availableCount = 0;
         if (datasetList != null) {
             for (int i = 0; i < datasetList.size(); i++) {
@@ -225,10 +229,34 @@
         });
     }
 
+    public void maybeSetSelectedDatasetId(int selectedDatasetId) {
+        mEventInternal.ifPresent(event -> {
+            event.mSelectedDatasetId = selectedDatasetId;
+        });
+    }
+
+    public void maybeSetDialogDismissed(boolean dialogDismissed) {
+        mEventInternal.ifPresent(event -> {
+            event.mDialogDismissed = dialogDismissed;
+        });
+    }
+
+    public void maybeSetNegativeCtaButtonClicked(boolean negativeCtaButtonClicked) {
+        mEventInternal.ifPresent(event -> {
+            event.mNegativeCtaButtonClicked = negativeCtaButtonClicked;
+        });
+    }
+
+    public void maybeSetPositiveCtaButtonClicked(boolean positiveCtaButtonClicked) {
+        mEventInternal.ifPresent(event -> {
+            event.mPositiveCtaButtonClicked = positiveCtaButtonClicked;
+        });
+    }
+
     public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
         mEventInternal.ifPresent(event -> {
             event.mDisplayPresentationType =
-                AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
+                    AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
             String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
                     Settings.Secure.DEFAULT_INPUT_METHOD, userId);
             if (TextUtils.isEmpty(imeString)) {
@@ -290,7 +318,11 @@
                     + " mFillRequestSentTimestampMs=" + event.mFillRequestSentTimestampMs
                     + " mFillResponseReceivedTimestampMs=" + event.mFillResponseReceivedTimestampMs
                     + " mSuggestionSentTimestampMs=" + event.mSuggestionSentTimestampMs
-                    + " mSuggestionPresentedTimestampMs=" + event.mSuggestionPresentedTimestampMs);
+                    + " mSuggestionPresentedTimestampMs=" + event.mSuggestionPresentedTimestampMs
+                    + " mSelectedDatasetId=" + event.mSelectedDatasetId
+                    + " mDialogDismissed=" + event.mDialogDismissed
+                    + " mNegativeCtaButtonClicked=" + event.mNegativeCtaButtonClicked
+                    + " mPositiveCtaButtonClicked=" + event.mPositiveCtaButtonClicked);
         }
 
         // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -316,11 +348,10 @@
                 event.mFillResponseReceivedTimestampMs,
                 event.mSuggestionSentTimestampMs,
                 event.mSuggestionPresentedTimestampMs,
-                //TODO(b/265051751): add new framework logging.
-                /* selected_dataset_id= */ 0,
-                /* dialog_dismissed= */ false,
-                /* negative_cta_button_clicked= */ false,
-                /* positive_cta_button_clicked= */ false);
+                event.mSelectedDatasetId,
+                event.mDialogDismissed,
+                event.mNegativeCtaButtonClicked,
+                event.mPositiveCtaButtonClicked);
         mEventInternal = Optional.empty();
     }
 
@@ -341,6 +372,10 @@
         int mFillResponseReceivedTimestampMs;
         int mSuggestionSentTimestampMs;
         int mSuggestionPresentedTimestampMs;
+        int mSelectedDatasetId = -1;
+        boolean mDialogDismissed = false;
+        boolean mNegativeCtaButtonClicked = false;
+        boolean mPositiveCtaButtonClicked = false;
 
         PresentationStatsEventInternal() {}
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 15a533c..4acdabe 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -41,6 +41,9 @@
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER;
+import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_PRE_TRIGGER;
+import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
 import static com.android.server.autofill.Helper.containsCharsInOrder;
 import static com.android.server.autofill.Helper.createSanitizers;
 import static com.android.server.autofill.Helper.getNumericValue;
@@ -439,6 +442,10 @@
     @GuardedBy("mLock")
     private PresentationStatsEventLogger mPresentationStatsEventLogger;
 
+    @NonNull
+    @GuardedBy("mLock")
+    private FillRequestEventLogger mFillRequestEventLogger;
+
     /**
      * Fill dialog request would likely be sent slightly later.
      */
@@ -605,6 +612,14 @@
             mPendingInlineSuggestionsRequest = null;
             mWaitForInlineRequest = false;
             mPendingFillRequest = null;
+
+            final long fillRequestSentRelativeTimestamp =
+                    SystemClock.elapsedRealtime() - mLatencyBaseTime;
+            mPresentationStatsEventLogger.maybeSetFillRequestSentTimestampMs(
+                    (int) (fillRequestSentRelativeTimestamp));
+            mFillRequestEventLogger.maybeSetLatencyFillRequestSentMillis(
+                    (int) (fillRequestSentRelativeTimestamp));
+            mFillRequestEventLogger.logAndEndEvent();
         }
 
         @Override
@@ -1082,11 +1097,21 @@
     private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
             int flags) {
         final FillResponse existingResponse = viewState.getResponse();
+        mFillRequestEventLogger.startLogForNewRequest();
+        mFillRequestEventLogger.maybeSetAppPackageUid(uid);
+        mFillRequestEventLogger.maybeSetFlags(mFlags);
+        if(mPreviouslyFillDialogPotentiallyStarted) {
+            mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_PRE_TRIGGER);
+        } else {
+            mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_NORMAL_TRIGGER);
+        }
         if (existingResponse != null) {
             setViewStatesLocked(
                     existingResponse,
                     ViewState.STATE_INITIAL,
                     /* clearResponse= */ true);
+            mFillRequestEventLogger.maybeSetRequestTriggerReason(
+                    TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE);
         }
         mSessionFlags.mExpiredResponse = false;
         mSessionState = STATE_ACTIVE;
@@ -1097,6 +1122,10 @@
                         + ", flags=" + flags + ")");
             }
             mSessionFlags.mAugmentedAutofillOnly = true;
+            // Augmented autofill doesn't have request_id.
+            mFillRequestEventLogger.maybeSetRequestId(-1);
+            mFillRequestEventLogger.maybeSetIsAugmented(mSessionFlags.mAugmentedAutofillOnly);
+            mFillRequestEventLogger.logAndEndEvent();
             triggerAugmentedAutofillLocked(flags);
             return;
         }
@@ -1123,6 +1152,12 @@
                     + ", flags=" + flags);
         }
         mPresentationStatsEventLogger.maybeSetRequestId(requestId);
+        mFillRequestEventLogger.maybeSetRequestId(requestId);
+        mFillRequestEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
+        if (mSessionFlags.mInlineSupportedByService) {
+            mFillRequestEventLogger.maybeSetInlineSuggestionHostUid(mContext, userId);
+        }
+        mFillRequestEventLogger.maybeSetIsFillDialogEligible(!mSessionFlags.mFillDialogDisabled);
 
         // If the focus changes very quickly before the first request is returned each focus change
         // triggers a new partition and we end up with many duplicate partitions. This is
@@ -1189,11 +1224,6 @@
             return;
         }
 
-        final long fillRequestSentRelativeTimestamp =
-                SystemClock.elapsedRealtime() - mLatencyBaseTime;
-        mPresentationStatsEventLogger.maybeSetFillRequestSentTimestampMs(
-                (int) (fillRequestSentRelativeTimestamp));
-
         // Now request the assist structure data.
         requestAssistStructureLocked(requestId, flags);
     }
@@ -1284,6 +1314,7 @@
         mCompatMode = compatMode;
         mSessionState = STATE_ACTIVE;
         mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
+        mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
         synchronized (mLock) {
             mSessionFlags = new SessionFlags();
             mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4a0a228..c129938 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -602,7 +602,7 @@
                     try {
                         final ServiceRecord.StartItem si = r.pendingStarts.get(0);
                         startServiceInnerLocked(this, si.intent, r, false, true, si.callingId,
-                                si.mCallingProcessName, r.startRequested);
+                                si.mCallingProcessName, r.startRequested, si.mCallingPackageName);
                     } catch (TransactionTooLargeException e) {
                         // Ignore, nobody upstack cares.
                     }
@@ -969,7 +969,7 @@
                 startServiceInnerLocked(r, service, callingUid, callingPid,
                         getCallingProcessNameLocked(callingUid, callingPid, callingPackage),
                         fgRequired, callerFg,
-                        backgroundStartPrivileges);
+                        backgroundStartPrivileges, callingPackage);
         if (res.aliasComponent != null
                 && !realResult.getPackageName().startsWith("!")
                 && !realResult.getPackageName().startsWith("?")) {
@@ -990,7 +990,7 @@
     private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service,
             int callingUid, int callingPid, String callingProcessName, boolean fgRequired,
             boolean callerFg,
-            BackgroundStartPrivileges backgroundStartPrivileges)
+            BackgroundStartPrivileges backgroundStartPrivileges, String callingPackage)
             throws TransactionTooLargeException {
         NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
                 service, callingUid, r.packageName, r.userId);
@@ -1003,7 +1003,7 @@
         r.delayedStop = false;
         r.fgRequired = fgRequired;
         r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
-                service, neededGrants, callingUid, callingProcessName));
+                service, neededGrants, callingUid, callingProcessName, callingPackage));
 
         if (fgRequired) {
             // We are now effectively running a foreground service.
@@ -1088,7 +1088,7 @@
             r.allowBgActivityStartsOnServiceStart(backgroundStartPrivileges);
         }
         ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting,
-                callingUid, callingProcessName, wasStartRequested);
+                callingUid, callingProcessName, wasStartRequested, callingPackage);
         return cmp;
     }
 
@@ -1241,7 +1241,7 @@
                         try {
                             startServiceInnerLocked(s, serviceIntent, callingUid, callingPid,
                                     callingProcessName, fgRequired, callerFg,
-                                    backgroundStartPrivileges);
+                                    backgroundStartPrivileges, callingPackage);
                         } catch (TransactionTooLargeException e) {
                             /* ignore - local call */
                         }
@@ -1287,7 +1287,7 @@
 
     ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
             boolean callerFg, boolean addToStarting, int callingUid, String callingProcessName,
-            boolean wasStartRequested) throws TransactionTooLargeException {
+            boolean wasStartRequested, String callingPackage) throws TransactionTooLargeException {
         synchronized (mAm.mProcessStats.mLock) {
             final ServiceState stracker = r.getTracker();
             if (stracker != null) {
@@ -1328,7 +1328,9 @@
                 : SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM),
                 getShortProcessNameForStats(callingUid, callingProcessName),
                 getShortServiceNameForStats(r),
-                packageState);
+                packageState,
+                packageName,
+                callingPackage);
 
         if (r.startRequested && addToStarting) {
             boolean first = smap.mStartingBackground.size() == 0;
@@ -3661,7 +3663,9 @@
                     : SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM),
                     getShortProcessNameForStats(callingUid, callerApp.processName),
                     getShortServiceNameForStats(s),
-                    packageState);
+                    packageState,
+                    s.packageName,
+                    callerApp.info.packageName);
 
             if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
                     + ": received=" + b.intent.received
@@ -5253,7 +5257,7 @@
         // be called.
         if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
             r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
-                    null, null, 0, null));
+                    null, null, 0, null, null));
         }
 
         sendServiceArgsLocked(r, execInFg, true);
@@ -6247,7 +6251,7 @@
                     stopServiceLocked(sr, true);
                 } else {
                     sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
-                            sr.getLastStartId(), baseIntent, null, 0, null));
+                            sr.getLastStartId(), baseIntent, null, 0, null, null));
                     if (sr.app != null && sr.app.getThread() != null) {
                         // We always run in the foreground, since this is called as
                         // part of the "remove task" UI operation.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 59a4139..9e5acf7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13933,7 +13933,7 @@
                 if (!sdkSandboxManagerLocal.canRegisterBroadcastReceiver(
                         /*IntentFilter=*/ filter, flags, onlyProtectedBroadcasts)) {
                     throw new SecurityException("SDK sandbox not allowed to register receiver"
-                            + " with the given IntentFilter");
+                            + " with the given IntentFilter: " + filter.toString());
                 }
             }
 
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 2d779c4..6928bd3 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -106,7 +106,6 @@
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -117,7 +116,6 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ServiceInfo;
 import android.content.pm.ServiceInfo.ForegroundServiceType;
-import android.database.ContentObserver;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AppBackgroundRestrictionsInfo;
@@ -137,7 +135,6 @@
 import android.provider.DeviceConfig.OnPropertiesChangedListener;
 import android.provider.DeviceConfig.Properties;
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -1069,8 +1066,7 @@
         }
     }
 
-    final class ConstantsObserver extends ContentObserver implements
-            OnPropertiesChangedListener {
+    final class ConstantsObserver implements OnPropertiesChangedListener {
         /**
          * Whether or not to set the app to restricted standby bucket automatically
          * when it's background-restricted.
@@ -1181,8 +1177,6 @@
 
         volatile boolean mBgAutoRestrictAbusiveApps;
 
-        volatile boolean mRestrictedBucketEnabled;
-
         volatile long mBgAbusiveNotificationMinIntervalMs;
 
         volatile long mBgLongFgsNotificationMinIntervalMs;
@@ -1215,7 +1209,6 @@
         volatile boolean mBgPromptAbusiveAppsToBgRestricted;
 
         ConstantsObserver(Handler handler, Context context) {
-            super(handler);
             mDefaultBgPromptFgsWithNotiToBgRestricted = context.getResources().getBoolean(
                     com.android.internal.R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted);
             mDefaultBgPromptAbusiveAppToBgRestricted = context.getResources().getBoolean(
@@ -1261,29 +1254,10 @@
             }
         }
 
-        @Override
-        public void onChange(boolean selfChange) {
-            updateSettings();
-        }
-
         public void start() {
-            final ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET),
-                    false, this);
-            updateSettings();
             updateDeviceConfig();
         }
 
-        void updateSettings() {
-            mRestrictedBucketEnabled = isRestrictedBucketEnabled();
-        }
-
-        private boolean isRestrictedBucketEnabled() {
-            return Global.getInt(mContext.getContentResolver(),
-                    Global.ENABLE_RESTRICTED_BUCKET,
-                    Global.DEFAULT_ENABLE_RESTRICTED_BUCKET) == 1;
-        }
-
         void updateDeviceConfig() {
             updateBgAutoRestrictedBucketChanged();
             updateBgAutoRestrictAbusiveApps();
@@ -1763,8 +1737,7 @@
                         .isAppBackgroundRestricted(uid, packageName)) {
                     return new Pair<>(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED, mEmptyTrackerInfo);
                 }
-                level = mConstantsObserver.mRestrictedBucketEnabled
-                        && standbyBucket == STANDBY_BUCKET_RESTRICTED
+                level = standbyBucket == STANDBY_BUCKET_RESTRICTED
                         ? RESTRICTION_LEVEL_RESTRICTED_BUCKET
                         : RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
                 if (calcTrackers) {
@@ -1811,13 +1784,9 @@
         @RestrictionLevel int level = RESTRICTION_LEVEL_UNKNOWN;
         @RestrictionLevel int prevLevel = level;
         BaseAppStateTracker resultTracker = null;
-        final boolean isRestrictedBucketEnabled = mConstantsObserver.mRestrictedBucketEnabled;
         for (int i = mAppStateTrackers.size() - 1; i >= 0; i--) {
             @RestrictionLevel int l = mAppStateTrackers.get(i).getPolicy()
                     .getProposedRestrictionLevel(packageName, uid, maxLevel);
-            if (!isRestrictedBucketEnabled && l == RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
-                l = RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
-            }
             level = Math.max(level, l);
             if (level != prevLevel) {
                 resultTracker = mAppStateTrackers.get(i);
@@ -2193,9 +2162,6 @@
         }
         if (level >= RESTRICTION_LEVEL_RESTRICTED_BUCKET
                 && curLevel < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
-            if (!mConstantsObserver.mRestrictedBucketEnabled) {
-                return;
-            }
             // Moving the app standby bucket to restricted in the meanwhile.
             if (DEBUG_BG_RESTRICTION_CONTROLLER
                     && level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 33d4004..4d46963 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -242,7 +242,7 @@
      */
     public boolean CORE_DEFER_UNTIL_ACTIVE = DEFAULT_CORE_DEFER_UNTIL_ACTIVE;
     private static final String KEY_CORE_DEFER_UNTIL_ACTIVE = "bcast_core_defer_until_active";
-    private static final boolean DEFAULT_CORE_DEFER_UNTIL_ACTIVE = false;
+    private static final boolean DEFAULT_CORE_DEFER_UNTIL_ACTIVE = true;
 
     // Settings override tracking for this instance
     private String mSettingsKey;
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 1f65730..0cdd4e9 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -399,7 +399,8 @@
      * Update if this process is in the "cached" state, typically signaling that
      * broadcast dispatch should be paused or delayed.
      */
-    public void setProcessCached(boolean cached) {
+    @VisibleForTesting
+    void setProcessCached(boolean cached) {
         if (mProcessCached != cached) {
             mProcessCached = cached;
             invalidateRunnableAt();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index c085706..fcddff0 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -601,7 +601,9 @@
                     r.dispatchTime - r.enqueueTime,
                     r.receiverTime - r.dispatchTime,
                     finishTime - r.receiverTime,
-                    packageState);
+                    packageState,
+                    r.curApp.info.packageName,
+                    r.callerPackage);
         }
         if (state == BroadcastRecord.IDLE) {
             Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
@@ -780,7 +782,8 @@
                     BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
                     BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
                     dispatchDelay, receiveDelay, 0 /* finish_delay */,
-                    SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL);
+                    SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
+                    app != null ? app.info.packageName : null, callingPackage);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index ff4f9b9..1f0b162 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1122,7 +1122,7 @@
             }
 
             r.terminalCount++;
-            notifyFinishReceiver(queue, r, index, receiver);
+            notifyFinishReceiver(queue, app, r, index, receiver);
             checkFinished = true;
         }
         // When entire ordered broadcast finished, deliver final result
@@ -1595,9 +1595,10 @@
      * typically for internal bookkeeping.
      */
     private void notifyFinishReceiver(@Nullable BroadcastProcessQueue queue,
-            @NonNull BroadcastRecord r, int index, @NonNull Object receiver) {
+            @Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
+            @NonNull Object receiver) {
         if (r.wasDeliveryAttempted(index)) {
-            logBroadcastDeliveryEventReported(queue, r, index, receiver);
+            logBroadcastDeliveryEventReported(queue, app, r, index, receiver);
         }
 
         final boolean recordFinished = (r.terminalCount == r.receivers.size());
@@ -1607,7 +1608,8 @@
     }
 
     private void logBroadcastDeliveryEventReported(@Nullable BroadcastProcessQueue queue,
-            @NonNull BroadcastRecord r, int index, @NonNull Object receiver) {
+            @Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
+            @NonNull Object receiver) {
         // Report statistics for each individual receiver
         final int uid = getReceiverUid(receiver);
         final int senderUid = (r.callingUid == -1) ? Process.SYSTEM_UID : r.callingUid;
@@ -1633,7 +1635,8 @@
                     ? SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED
                     : SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
             FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, uid, senderUid, actionName,
-                    receiverType, type, dispatchDelay, receiveDelay, finishDelay, packageState);
+                    receiverType, type, dispatchDelay, receiveDelay, finishDelay, packageState,
+                    app != null ? app.info.packageName : null, r.callerPackage);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 3ab9e71..e8b65b8 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1914,7 +1914,9 @@
         uids[0] = uid;
         frozenStates[0] = frozen ? UID_FROZEN_STATE_FROZEN : UID_FROZEN_STATE_UNFROZEN;
 
-        Slog.d(TAG_AM, "reportOneUidFrozenStateChanged uid " + uid + " frozen = " + frozen);
+        if (DEBUG_FREEZER) {
+            Slog.d(TAG_AM, "reportOneUidFrozenStateChanged uid " + uid + " frozen = " + frozen);
+        }
 
         mAm.reportUidFrozenStateChanged(uids, frozenStates);
     }
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 48df1494..a1fcd42 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -263,7 +263,8 @@
                             PROVIDER_ACQUISITION_EVENT_REPORTED,
                             r.uid, callingUid,
                             PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
-                            PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL);
+                            PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
+                            cpi.packageName, callingPackage);
                     return holder;
                 }
 
@@ -334,7 +335,8 @@
                                 PROVIDER_ACQUISITION_EVENT_REPORTED,
                                 cpr.proc.uid, callingUid,
                                 PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
-                                PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL);
+                                PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
+                                cpi.packageName, callingPackage);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(origId);
@@ -511,7 +513,8 @@
                                     PROVIDER_ACQUISITION_EVENT_REPORTED,
                                     proc.uid, callingUid,
                                     PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
-                                    PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL);
+                                    PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
+                                    cpi.packageName, callingPackage);
                         } else {
                             final int packageState =
                                     ((cpr.appInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0)
@@ -536,7 +539,7 @@
                                     PROVIDER_ACQUISITION_EVENT_REPORTED,
                                     proc.uid, callingUid,
                                     PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
-                                    packageState);
+                                    packageState, cpi.packageName, callingPackage);
                         }
                         cpr.launchingApp = proc;
                         mLaunchingProviders.add(cpr);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 663121e..4defdc6 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -249,6 +249,7 @@
         final String mCallingProcessName;
         final Intent intent;
         final NeededUriGrants neededGrants;
+        final @Nullable String mCallingPackageName;
         long deliveredTime;
         int deliveryCount;
         int doneExecutingCount;
@@ -258,7 +259,7 @@
 
         StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id,
                 Intent _intent, NeededUriGrants _neededGrants, int _callingId,
-                String callingProcessName) {
+                String callingProcessName, @Nullable String callingPackageName) {
             sr = _sr;
             taskRemoved = _taskRemoved;
             id = _id;
@@ -266,6 +267,7 @@
             neededGrants = _neededGrants;
             callingId = _callingId;
             mCallingProcessName = callingProcessName;
+            mCallingPackageName = callingPackageName;
         }
 
         UriPermissionOwner getUriPermissionsLocked() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 127a9d8b..4e687f4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1046,9 +1046,14 @@
 
         mSfxHelper = new SoundEffectsHelper(mContext, playerBase -> ignorePlayerLogs(playerBase));
 
-        final boolean headTrackingDefault = mContext.getResources().getBoolean(
+        final boolean binauralEnabledDefault = SystemProperties.getBoolean(
+                "ro.audio.spatializer_binaural_enabled_default", true);
+        final boolean transauralEnabledDefault = SystemProperties.getBoolean(
+                "ro.audio.spatializer_transaural_enabled_default", true);
+        final boolean headTrackingEnabledDefault = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_spatial_audio_head_tracking_enabled_default);
-        mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, headTrackingDefault);
+        mSpatializerHelper = new SpatializerHelper(this, mAudioSystem,
+                binauralEnabledDefault, transauralEnabledDefault, headTrackingEnabledDefault);
 
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
@@ -7237,7 +7242,7 @@
         super.setDeviceVolumeBehavior_enforcePermission();
         // verify arguments
         Objects.requireNonNull(device);
-        AudioManager.enforceSettableVolumeBehavior(deviceVolumeBehavior);
+        AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
 
         sVolumeLogger.enqueue(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
                 + AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 8f54e45..5edd434 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -172,13 +172,17 @@
     // initialization
     @SuppressWarnings("StaticAssignmentInConstructor")
     SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa,
-            boolean headTrackingEnabledByDefault) {
+            boolean binauralEnabledDefault,
+            boolean transauralEnabledDefault,
+            boolean headTrackingEnabledDefault) {
         mAudioService = mother;
         mASA = asa;
         // "StaticAssignmentInConstructor" warning is suppressed as the SpatializerHelper being
         // constructed here is the factory for SADeviceState, thus SADeviceState and its
         // private static field sHeadTrackingEnabledDefault should never be accessed directly.
-        SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledByDefault;
+        SADeviceState.sBinauralEnabledDefault = binauralEnabledDefault;
+        SADeviceState.sTransauralEnabledDefault = transauralEnabledDefault;
+        SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledDefault;
     }
 
     synchronized void init(boolean effectExpected, @Nullable String settings) {
@@ -1547,10 +1551,12 @@
     }
 
     /*package*/ static final class SADeviceState {
+        private static boolean sBinauralEnabledDefault = true;
+        private static boolean sTransauralEnabledDefault = true;
         private static boolean sHeadTrackingEnabledDefault = false;
         final @AudioDeviceInfo.AudioDeviceType int mDeviceType;
         final @NonNull String mDeviceAddress;
-        boolean mEnabled = true;               // by default, SA is enabled on any device
+        boolean mEnabled;
         boolean mHasHeadTracker = false;
         boolean mHeadTrackerEnabled;
         static final String SETTING_FIELD_SEPARATOR = ",";
@@ -1566,6 +1572,12 @@
         SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @Nullable String address) {
             mDeviceType = deviceType;
             mDeviceAddress = isWireless(deviceType) ? Objects.requireNonNull(address) : "";
+            final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
+            mEnabled = spatMode == SpatializationMode.SPATIALIZER_BINAURAL
+                    ? sBinauralEnabledDefault
+                    : spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL
+                            ? sTransauralEnabledDefault
+                            : false;
             mHeadTrackerEnabled = sHeadTrackingEnabledDefault;
         }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b336b95..7a0bf0c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2003,7 +2003,7 @@
     @Override
     public InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(
+            mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         synchronized (ImfLock.class) {
@@ -2017,7 +2017,7 @@
     public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
             @DirectBootAwareness int directBootAwareness) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(
+            mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         synchronized (ImfLock.class) {
@@ -2040,7 +2040,7 @@
     @Override
     public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(
+            mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         synchronized (ImfLock.class) {
@@ -2062,7 +2062,7 @@
     @Override
     public boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(
+            mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
 
@@ -2158,7 +2158,8 @@
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
             boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
 
         synchronized (ImfLock.class) {
@@ -3634,7 +3635,8 @@
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
             @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
 
             if (editorInfo == null || editorInfo.targetInputMethodUser == null
                     || editorInfo.targetInputMethodUser.getIdentifier() != userId) {
@@ -4115,7 +4117,8 @@
     @Override
     public InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         synchronized (ImfLock.class) {
             if (mSettings.getCurrentUserId() == userId) {
@@ -4133,7 +4136,8 @@
     public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
             @UserIdInt int userId) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         final int callingUid = Binder.getCallingUid();
 
@@ -4186,7 +4190,8 @@
     public void setExplicitlyEnabledInputMethodSubtypes(String imeId,
             @NonNull int[] subtypeHashCodes, @UserIdInt int userId) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         final int callingUid = Binder.getCallingUid();
         final ComponentName imeComponentName =
@@ -5412,7 +5417,8 @@
     @Override
     public InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         synchronized (ImfLock.class) {
             if (mSettings.getCurrentUserId() == userId) {
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 647a89e..6f0903c 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -25,6 +25,7 @@
 import android.content.IntentFilter;
 import android.telecom.TelecomManager;
 
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.util.NotificationMessagingUtil;
 
 import java.util.Comparator;
@@ -38,6 +39,7 @@
 
     private final Context mContext;
     private final NotificationMessagingUtil mMessagingUtil;
+    private final boolean mSortByInterruptiveness;
     private String mDefaultPhoneApp;
 
     public NotificationComparator(Context context) {
@@ -45,6 +47,8 @@
         mContext.registerReceiver(mPhoneAppBroadcastReceiver,
                 new IntentFilter(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED));
         mMessagingUtil = new NotificationMessagingUtil(mContext);
+        mSortByInterruptiveness = !SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+                SystemUiSystemPropertiesFlags.NotificationFlags.NO_SORT_BY_INTERRUPTIVENESS);
     }
 
     @Override
@@ -135,10 +139,12 @@
             return -1 * Integer.compare(leftPriority, rightPriority);
         }
 
-        final boolean leftInterruptive = left.isInterruptive();
-        final boolean rightInterruptive = right.isInterruptive();
-        if (leftInterruptive != rightInterruptive) {
-            return -1 * Boolean.compare(leftInterruptive, rightInterruptive);
+        if (mSortByInterruptiveness) {
+            final boolean leftInterruptive = left.isInterruptive();
+            final boolean rightInterruptive = right.isInterruptive();
+            if (leftInterruptive != rightInterruptive) {
+                return -1 * Boolean.compare(leftInterruptive, rightInterruptive);
+            }
         }
 
         // then break ties by time, most recent first
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c607eca..f09f7c2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7803,7 +7803,8 @@
      */
     @GuardedBy("mNotificationLock")
     @VisibleForTesting
-    protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+    protected boolean isVisuallyInterruptive(@Nullable NotificationRecord old,
+            @NonNull NotificationRecord r) {
         // Ignore summary updates because we don't display most of the information.
         if (r.getSbn().isGroup() && r.getSbn().getNotification().isGroupSummary()) {
             if (DEBUG_INTERRUPTIVENESS) {
@@ -7821,14 +7822,6 @@
             return true;
         }
 
-        if (r == null) {
-            if (DEBUG_INTERRUPTIVENESS) {
-                Slog.v(TAG, "INTERRUPTIVENESS: "
-                        +  r.getKey() + " is not interruptive: null");
-            }
-            return false;
-        }
-
         Notification oldN = old.getSbn().getNotification();
         Notification newN = r.getSbn().getNotification();
         if (oldN.extras == null || newN.extras == null) {
@@ -7886,6 +7879,14 @@
             return true;
         }
 
+        if (Notification.areIconsDifferent(oldN, newN)) {
+            if (DEBUG_INTERRUPTIVENESS) {
+                Slog.v(TAG, "INTERRUPTIVENESS: "
+                        +  r.getKey() + " is interruptive: icons differ");
+            }
+            return true;
+        }
+
         // Fields below are invisible to bubbles.
         if (r.canBubble()) {
             if (DEBUG_INTERRUPTIVENESS) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7fe6c7d..cd1ae74 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3279,7 +3279,7 @@
         final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
         removePackageHelper.removePackage(stubPkg, true /*chatty*/);
         try {
-            return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null);
+            return initPackageTracedLI(scanFile, parseFlags, scanFlags);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
                     e);
@@ -3410,8 +3410,7 @@
                         | ParsingPackageUtils.PARSE_MUST_BE_APK
                         | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
         @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
-        final AndroidPackage pkg = scanSystemPackageTracedLI(
-                codePath, parseFlags, scanFlags, null);
+        final AndroidPackage pkg = initPackageTracedLI(codePath, parseFlags, scanFlags);
 
         synchronized (mPm.mLock) {
             PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3591,7 +3590,7 @@
                 try {
                     final File codePath = new File(pkg.getPath());
                     synchronized (mPm.mInstallLock) {
-                        scanSystemPackageTracedLI(codePath, 0, scanFlags, null);
+                        initPackageTracedLI(codePath, 0, scanFlags);
                     }
                 } catch (PackageManagerException e) {
                     Slog.e(TAG, "Failed to parse updated, ex-system package: "
@@ -3734,12 +3733,6 @@
             String errorMsg = null;
 
             if (throwable == null) {
-                // TODO(b/194319951): move lower in the scan chain
-                // Static shared libraries have synthetic package names
-                if (parseResult.parsedPackage.isStaticSharedLibrary()) {
-                    PackageManagerService.renameStaticSharedLibraryPackage(
-                            parseResult.parsedPackage);
-                }
                 try {
                     addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
                             new UserHandle(UserHandle.USER_SYSTEM), apexInfo);
@@ -3804,8 +3797,8 @@
 
             try {
                 synchronized (mPm.mInstallLock) {
-                    final AndroidPackage newPkg = scanSystemPackageTracedLI(
-                            scanFile, reparseFlags, rescanFlags, null);
+                    final AndroidPackage newPkg = initPackageTracedLI(
+                            scanFile, reparseFlags, rescanFlags);
                     // We rescanned a stub, add it to the list of stubbed system packages
                     if (newPkg.isStub()) {
                         stubSystemApps.add(packageName);
@@ -3819,28 +3812,26 @@
     }
 
     /**
-     *  Traces a package scan.
-     *  @see #scanSystemPackageLI(File, int, int, UserHandle)
+     *  Traces a package scan and registers it with the system.
+     *  @see #initPackageLI(File, int, int)
      */
     @GuardedBy("mPm.mInstallLock")
-    public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags,
-            int scanFlags, @Nullable ApexManager.ActiveApexInfo apexInfo)
+    public AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags)
             throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
         try {
-            return scanSystemPackageLI(scanFile, parseFlags, scanFlags, apexInfo);
+            return initPackageLI(scanFile, parseFlags, scanFlags);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
     /**
-     *  Scans a package and returns the newly parsed package.
+     *  Scans a package, registers it with the system and returns the newly parsed package.
      *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
      */
     @GuardedBy("mPm.mInstallLock")
-    private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags,
-            @Nullable ApexManager.ActiveApexInfo apexInfo)
+    private AndroidPackage initPackageLI(File scanFile, int parseFlags, int scanFlags)
             throws PackageManagerException {
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
 
@@ -3852,13 +3843,8 @@
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
-        // Static shared libraries have synthetic package names
-        if (parsedPackage.isStaticSharedLibrary()) {
-            PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
-        }
-
         return addForInitLI(parsedPackage, parseFlags, scanFlags,
-                new UserHandle(UserHandle.USER_SYSTEM), apexInfo);
+                new UserHandle(UserHandle.USER_SYSTEM), null);
     }
 
     /**
@@ -3882,6 +3868,10 @@
             throws PackageManagerException {
         PackageSetting disabledPkgSetting;
         synchronized (mPm.mLock) {
+            // Static shared libraries have synthetic package names
+            if (activeApexInfo == null && parsedPackage.isStaticSharedLibrary()) {
+                PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
+            }
             disabledPkgSetting =
                     mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
             if (activeApexInfo != null && disabledPkgSetting != null) {
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 7684a49..8f8f437 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -159,8 +159,8 @@
             synchronized (mPm.mInstallLock) {
                 final AndroidPackage pkg;
                 try {
-                    pkg = installPackageHelper.scanSystemPackageTracedLI(
-                            ps.getPath(), parseFlags, SCAN_INITIAL, null);
+                    pkg = installPackageHelper.initPackageTracedLI(
+                            ps.getPath(), parseFlags, SCAN_INITIAL);
                     loaded.add(pkg);
 
                 } catch (PackageManagerException e) {
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index e146135..76a714c 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -93,7 +93,8 @@
         mGlobalActionsAvailable = available;
         if (mShowing && !mGlobalActionsAvailable) {
             // Global actions provider died but we need to be showing global actions still, show the
-            // legacy global acrions provider.
+            // legacy global actions provider and remove timeout callbacks to avoid legacy re-show.
+            mHandler.removeCallbacks(mShowTimeout);
             ensureLegacyCreated();
             mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3eeafeb..b32e8f0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1537,7 +1537,9 @@
 
     private void interceptScreenshotChord(int source, long pressDelay) {
         mHandler.removeMessages(MSG_SCREENSHOT_CHORD);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCREENSHOT_CHORD, source),
+        // arg2 is unused, but necessary to insure we call the correct method signature
+        // since the screenshot source is read from message.arg1
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCREENSHOT_CHORD, source, 0),
                 pressDelay);
     }
 
@@ -3555,16 +3557,16 @@
             mPendingKeyguardOccluded = occluded;
             mKeyguardOccludedChanged = true;
         } else {
-            setKeyguardOccludedLw(occluded, true /* notify */);
+            setKeyguardOccludedLw(occluded);
         }
     }
 
     @Override
-    public int applyKeyguardOcclusionChange(boolean notify) {
+    public int applyKeyguardOcclusionChange() {
         if (mKeyguardOccludedChanged) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
                     + mPendingKeyguardOccluded);
-            if (setKeyguardOccludedLw(mPendingKeyguardOccluded, notify)) {
+            if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
                 return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
             }
         }
@@ -3583,8 +3585,10 @@
      */
     private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
             boolean notifyOccluded) {
-        final int redoLayout = applyKeyguardOcclusionChange(notifyOccluded);
-        if (redoLayout != 0) return redoLayout;
+        if (notifyOccluded) {
+            final int redoLayout = applyKeyguardOcclusionChange();
+            if (redoLayout != 0) return redoLayout;
+        }
         if (startKeyguardExitAnimation) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
             startKeyguardExitAnimation(SystemClock.uptimeMillis());
@@ -3835,20 +3839,16 @@
     }
 
     /**
-     * Updates the occluded state of the Keyguard.
+     * Updates the occluded state of the Keyguard immediately via
+     * {@link com.android.internal.policy.IKeyguardService}.
      *
      * @param isOccluded Whether the Keyguard is occluded by another window.
-     * @param notify Notify keyguard occlude status change immediately via
-     *       {@link com.android.internal.policy.IKeyguardService}.
      * @return Whether the flags have changed and we have to redo the layout.
      */
-    private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) {
+    private boolean setKeyguardOccludedLw(boolean isOccluded) {
         if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
         mKeyguardOccludedChanged = false;
-        if (isKeyguardOccluded() == isOccluded) {
-            return false;
-        }
-        mKeyguardDelegate.setOccluded(isOccluded, notify);
+        mKeyguardDelegate.setOccluded(isOccluded, true /* notify */);
         return mKeyguardDelegate.isShowing();
     }
 
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 3c4dbf2..5d558e9 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -170,10 +170,10 @@
     void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition);
 
     /**
-     * @param notify {@code true} if the status change should be immediately notified via
-     *        {@link com.android.internal.policy.IKeyguardService}
+     * Commit any queued changes to keyguard occlude status that had been deferred during the
+     * start of an animation or transition.
      */
-    int applyKeyguardOcclusionChange(boolean notify);
+    int applyKeyguardOcclusionChange();
 
     /**
      * Interface to the Window Manager state associated with a particular
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d08293b..f5bc8ff 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -121,6 +121,7 @@
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_OLD_UNSET;
+import static android.view.WindowManager.TRANSIT_RELAUNCH;
 import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
@@ -5205,7 +5206,7 @@
             Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
             return;
         }
-        if (visible == mVisibleRequested && visible == mVisible
+        if (visible == mVisibleRequested && visible == mVisible && visible == isClientVisible()
                 && mTransitionController.isShellTransitionsEnabled()) {
             // For shell transition, it is no-op if there is no state change.
             return;
@@ -8362,6 +8363,10 @@
      * requested in the config or via an ADB command. For more context see {@link
      * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)} and
      * {@link LetterboxUiController#getVerticalPositionMultiplier(Configuration)}
+     * <p>
+     * Note that this is the final step that can change the resolved bounds. After this method
+     * is called, the position of the bounds will be moved to app space as sandboxing if the
+     * activity has a size compat scale.
      */
     private void updateResolvedBoundsPosition(Configuration newParentConfiguration) {
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
@@ -8423,6 +8428,18 @@
 
         // Since bounds has changed, the configuration needs to be computed accordingly.
         getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+
+        // The position of configuration bounds were calculated in screen space because that is
+        // easier to resolve the relative position in parent container. However, if the activity is
+        // scaled, the position should follow the scale because the configuration will be sent to
+        // the client which is expected to be in a scaled environment.
+        if (mSizeCompatScale != 1f) {
+            final int screenPosX = resolvedBounds.left;
+            final int screenPosY = resolvedBounds.top;
+            final int dx = (int) (screenPosX / mSizeCompatScale + 0.5f) - screenPosX;
+            final int dy = (int) (screenPosY / mSizeCompatScale + 0.5f) - screenPosY;
+            offsetBounds(resolvedConfig, dx, dy);
+        }
     }
 
     void recomputeConfiguration() {
@@ -9685,9 +9702,36 @@
             return;
         }
 
-        if (getParent() != null) {
+        if (mTransitionController.isShellTransitionsEnabled()) {
+            final Transition transition = new Transition(TRANSIT_RELAUNCH, 0 /* flags */,
+                    mTransitionController, mWmService.mSyncEngine);
+            final Runnable executeRestart = () -> {
+                if (mState != RESTARTING_PROCESS || !attachedToProcess()) {
+                    transition.abort();
+                    return;
+                }
+                // Request invisible so there will be a change after the activity is restarted
+                // to be visible.
+                setVisibleRequested(false);
+                transition.collect(this);
+                mTransitionController.requestStartTransition(transition, task,
+                        null /* remoteTransition */, null /* displayChange */);
+                scheduleStopForRestartProcess();
+            };
+            if (mWmService.mSyncEngine.hasActiveSync()) {
+                mWmService.mSyncEngine.queueSyncSet(
+                        () -> mTransitionController.moveToCollecting(transition), executeRestart);
+            } else {
+                mTransitionController.moveToCollecting(transition);
+                executeRestart.run();
+            }
+        } else {
             startFreezingScreen();
+            scheduleStopForRestartProcess();
         }
+    }
+
+    private void scheduleStopForRestartProcess() {
         // The process will be killed until the activity reports stopped with saved state (see
         // {@link ActivityTaskManagerService.activityStopped}).
         try {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index f8fb76a..5e066fa 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -50,6 +50,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.RemoteAnimationAdapter;
+import android.view.WindowManager;
+import android.window.RemoteTransition;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -560,8 +562,9 @@
         final Task rootTask = mService.mRootWindowContainer.getDefaultTaskDisplayArea()
                 .getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
         if (rootTask == null) return false;
+        final RemoteTransition remote = options.getRemoteTransition();
         final ActivityRecord r = rootTask.topRunningActivity();
-        if (r == null || r.isVisibleRequested() || !r.attachedToProcess()
+        if (r == null || r.isVisibleRequested() || !r.attachedToProcess() || remote == null
                 || !r.mActivityComponent.equals(intent.getComponent())
                 // Recents keeps invisible while device is locked.
                 || r.mDisplayContent.isKeyguardLocked()) {
@@ -570,13 +573,47 @@
         mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(true /* forceSend */, r);
         final ActivityMetricsLogger.LaunchingState launchingState =
                 mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
+        final Transition transition = new Transition(WindowManager.TRANSIT_TO_FRONT,
+                0 /* flags */, r.mTransitionController, mService.mWindowManager.mSyncEngine);
+        if (r.mTransitionController.isCollecting()) {
+            // Special case: we are entering recents while an existing transition is running. In
+            // this case, we know it's safe to "defer" the activity launch, so lets do so now so
+            // that it can get its own transition and thus update launcher correctly.
+            mService.mWindowManager.mSyncEngine.queueSyncSet(
+                    () -> {
+                        if (r.isAttached()) {
+                            r.mTransitionController.moveToCollecting(transition);
+                        }
+                    },
+                    () -> {
+                        if (r.isAttached() && transition.isCollecting()) {
+                            startExistingRecentsIfPossibleInner(options, r, rootTask,
+                                    launchingState, remote, transition);
+                        }
+                    });
+        } else {
+            r.mTransitionController.moveToCollecting(transition);
+            startExistingRecentsIfPossibleInner(options, r, rootTask, launchingState, remote,
+                    transition);
+        }
+        return true;
+    }
+
+    private void startExistingRecentsIfPossibleInner(ActivityOptions options, ActivityRecord r,
+            Task rootTask, ActivityMetricsLogger.LaunchingState launchingState,
+            RemoteTransition remoteTransition, Transition transition) {
         final Task task = r.getTask();
         mService.deferWindowLayout();
         try {
             final TransitionController controller = r.mTransitionController;
             if (controller.getTransitionPlayer() != null) {
+                controller.requestStartTransition(transition, task, remoteTransition,
+                        null /* displayChange */);
                 controller.collect(task);
                 controller.setTransientLaunch(r, TaskDisplayArea.getRootTaskAbove(rootTask));
+            } else {
+                // The transition player might be died when executing the queued transition.
+                transition.abort();
             }
             task.moveToFront("startExistingRecents");
             task.mInResumeTopActivity = true;
@@ -587,7 +624,6 @@
             task.mInResumeTopActivity = false;
             mService.continueWindowLayout();
         }
-        return true;
     }
 
     void registerRemoteAnimationForNextActivityStart(String packageName,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 12be1d3..ce29564 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1582,19 +1582,19 @@
             }
         }
         if (isTransientLaunch) {
-            if (forceTransientTransition) {
-                transitionController.collect(mLastStartActivityRecord);
-                transitionController.collect(mPriorAboveTask);
+            if (forceTransientTransition && newTransition != null) {
+                newTransition.collect(mLastStartActivityRecord);
+                newTransition.collect(mPriorAboveTask);
             }
             // `started` isn't guaranteed to be the actual relevant activity, so we must wait
             // until after we launched to identify the relevant activity.
             transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask);
-            if (forceTransientTransition) {
+            if (forceTransientTransition && newTransition != null) {
                 final DisplayContent dc = mLastStartActivityRecord.getDisplayContent();
                 // update wallpaper target to TransientHide
                 dc.mWallpaperController.adjustWallpaperWindows();
                 // execute transition because there is no change
-                transitionController.setReady(dc, true /* ready */);
+                newTransition.setReady(dc, true /* ready */);
             }
         }
         if (!userLeaving) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 555cd38..992743a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1240,6 +1240,25 @@
             ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
 
         final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
+        // A quick path (skip general intent/task resolving) to start recents animation if the
+        // recents (or home) activity is available in background.
+        if (opts != null && opts.getOriginalOptions().getTransientLaunch()
+                && isCallerRecents(Binder.getCallingUid())) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                synchronized (mGlobalLock) {
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "startExistingRecents");
+                    if (mActivityStartController.startExistingRecentsIfPossible(
+                            intent, opts.getOriginalOptions())) {
+                        return ActivityManager.START_TASK_TO_FRONT;
+                    }
+                    // Else follow the standard launch procedure.
+                }
+            } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
 
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityAsUser");
@@ -5699,23 +5718,6 @@
                 boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
                 BackgroundStartPrivileges backgroundStartPrivileges) {
             assertPackageMatchesCallingUid(callingPackage);
-            // A quick path (skip general intent/task resolving) to start recents animation if the
-            // recents (or home) activity is available in background.
-            if (options != null && options.getOriginalOptions() != null
-                    && options.getOriginalOptions().getTransientLaunch() && isCallerRecents(uid)) {
-                try {
-                    synchronized (mGlobalLock) {
-                        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "startExistingRecents");
-                        if (mActivityStartController.startExistingRecentsIfPossible(
-                                intent, options.getOriginalOptions())) {
-                            return ActivityManager.START_TASK_TO_FRONT;
-                        }
-                        // Else follow the standard launch procedure.
-                    }
-                } finally {
-                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-                }
-            }
             return getActivityStartController().startActivityInPackage(uid, realCallingPid,
                     realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
                     resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8c59548..8f40e79 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 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;
@@ -296,8 +297,11 @@
                 // be applied using the SurfaceControl hierarchy from the Organizer. This means
                 // we need to make sure that these changes in crop are reflected in the input
                 // windows, and so ensure this flag is set so that the input crop always reflects
-                // the surface hierarchy.
-                useSurfaceBoundsAsTouchRegion = true;
+                // the surface hierarchy. However, we only want to set this when the client did
+                // not already provide a touchable region, so that we don't ignore the one provided.
+                if (w.mTouchableInsets != TOUCHABLE_INSETS_REGION) {
+                    useSurfaceBoundsAsTouchRegion = true;
+                }
 
                 if (w.mAttrs.isModal()) {
                     TaskFragment parent = w.getTaskFragment();
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 980a941..ef464d2 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -841,7 +841,7 @@
         int dividerInsets =
                 getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_insets);
         int dividerSize = dividerWindowWidth - dividerInsets * 2;
-        final Rect bounds = new Rect(displayContent.getBounds());
+        final Rect bounds = new Rect(displayContent.getWindowConfiguration().getAppBounds());
         if (bounds.width() >= bounds.height()) {
             bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
             bounds.right = bounds.centerX();
@@ -1498,7 +1498,7 @@
     }
 
     private void inheritConfiguration(ActivityRecord firstOpaque) {
-        // To avoid wrong behaviour, we're not forcing a specific aspet ratio to activities
+        // To avoid wrong behaviour, we're not forcing a specific aspect ratio to activities
         // which are not already providing one (e.g. permission dialogs) and presumably also
         // not resizable.
         if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8c6de8e..68b2d0f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3361,6 +3361,8 @@
                 && info.pictureInPictureParams.isLaunchIntoPip()
                 && top.getLastParentBeforePip() != null)
                         ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID;
+        info.lastParentTaskIdBeforePip = top != null && top.getLastParentBeforePip() != null
+                ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID;
         info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays;
         info.mTopActivityLocusId = top != null ? top.getLocusId() : null;
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 362e1c8..ba49dd0 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1481,9 +1481,9 @@
             // then we have to notify KeyguardService directly. This can happen if there is
             // another ongoing transition when the app changes occlusion OR if the app dies or
             // is killed. Both of these are common during tests.
-            final boolean notify = !(transit == TRANSIT_KEYGUARD_OCCLUDE
-                    || transit == TRANSIT_KEYGUARD_UNOCCLUDE);
-            mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange(notify);
+            if (transit != TRANSIT_KEYGUARD_OCCLUDE && transit != TRANSIT_KEYGUARD_UNOCCLUDE) {
+                mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange();
+            }
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 8f9b949..a15d6630 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.credentials;
 
+import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS;
 import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
 import static android.Manifest.permission.LAUNCH_CREDENTIAL_SELECTOR;
 import static android.content.Context.CREDENTIAL_SERVICE;
@@ -124,7 +125,8 @@
         serviceInfos.forEach(
                 info -> {
                     services.add(
-                            new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
+                            new CredentialManagerServiceImpl(this, mLock, resolvedUserId,
+                                    info));
                 });
         return services;
     }
@@ -418,6 +420,7 @@
                 // Check privileged permissions
                 mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
             }
+            enforcePermissionForAllowedProviders(request);
 
             final int userId = UserHandle.getCallingUserId();
             final int callingUid = Binder.getCallingUid();
@@ -447,6 +450,9 @@
             // TODO(b/273308895): implement
 
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
+            enforcePermissionForAllowedProviders(request);
+
             return cancelTransport;
         }
 
@@ -823,6 +829,17 @@
         }
     }
 
+    private void enforcePermissionForAllowedProviders(GetCredentialRequest request) {
+        boolean containsAllowedProviders = request.getCredentialOptions()
+                .stream()
+                .anyMatch(option -> option.getAllowedProviders() != null
+                        && !option.getAllowedProviders().isEmpty());
+        if (containsAllowedProviders) {
+            mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS,
+                    null);
+        }
+    }
+
     private void enforceCallingPackage(String callingPackage, int callingUid) {
         int packageUid;
         PackageManager pm = mContext.createContextAsUser(
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 9bc5998..95b0ff0 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -94,7 +94,8 @@
             RemoteCredentialService remoteCredentialService) {
         android.credentials.GetCredentialRequest filteredRequest =
                 filterOptions(providerInfo.getCapabilities(),
-                        getRequestSession.mClientRequest);
+                        getRequestSession.mClientRequest,
+                        providerInfo.getComponentName());
         if (filteredRequest != null) {
             Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
                     new HashMap<>();
@@ -142,17 +143,19 @@
     @Nullable
     private static android.credentials.GetCredentialRequest filterOptions(
             List<String> providerCapabilities,
-            android.credentials.GetCredentialRequest clientRequest
+            android.credentials.GetCredentialRequest clientRequest,
+            ComponentName componentName
     ) {
         List<CredentialOption> filteredOptions = new ArrayList<>();
         for (CredentialOption option : clientRequest.getCredentialOptions()) {
-            if (providerCapabilities.contains(option.getType())) {
+            if (providerCapabilities.contains(option.getType())
+                    && isProviderAllowed(option, componentName)) {
                 Log.i(TAG, "In createProviderRequest - capability found : "
                         + option.getType());
                 filteredOptions.add(option);
             } else {
                 Log.i(TAG, "In createProviderRequest - capability not "
-                        + "found : " + option.getType());
+                        + "found, or provider not allowed : " + option.getType());
             }
         }
         if (!filteredOptions.isEmpty()) {
@@ -165,6 +168,16 @@
         return null;
     }
 
+    private static boolean isProviderAllowed(CredentialOption option, ComponentName componentName) {
+        if (!option.getAllowedProviders().isEmpty() && !option.getAllowedProviders().contains(
+                componentName)) {
+            Log.d(TAG, "Provider allow list specified but does not contain this provider: "
+                    + componentName.flattenToString());
+            return false;
+        }
+        return true;
+    }
+
     public ProviderGetSession(Context context,
             CredentialProviderInfo info,
             ProviderInternalCallback<GetCredentialResponse> callbacks,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 303de12..e9c50b5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11780,22 +11780,9 @@
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(canManageUsers(caller) || canQueryAdminPolicy(caller));
 
-        // Move AccessibilityManager out of lock to prevent potential deadlock
-        final List<AccessibilityServiceInfo> installedServices;
-        long id = mInjector.binderClearCallingIdentity();
-        try {
-            UserInfo user = getUserInfo(userId);
-            if (user.isManagedProfile()) {
-                userId = user.profileGroupId;
-            }
-            installedServices = withAccessibilityManager(userId,
-                    AccessibilityManager::getInstalledAccessibilityServiceList);
-        } finally {
-            mInjector.binderRestoreCallingIdentity(id);
-        }
+        List<String> result = null;
 
         synchronized (getLockObject()) {
-            List<String> result = null;
             // If we have multiple profiles we return the intersection of the
             // permitted lists. This can happen in cases where we have a device
             // and profile owner.
@@ -11817,9 +11804,22 @@
                     }
                 }
             }
+        }
 
-            // If we have a permitted list add all system accessibility services.
-            if (result != null) {
+        // If we have a permitted list add all system accessibility services.
+        if (result != null) {
+            long id = mInjector.binderClearCallingIdentity();
+            try {
+                UserInfo user = getUserInfo(userId);
+                if (user.isManagedProfile()) {
+                    userId = user.profileGroupId;
+                }
+                // Move AccessibilityManager out of {@link getLockObject} to prevent potential
+                // deadlock.
+                final List<AccessibilityServiceInfo> installedServices =
+                        withAccessibilityManager(userId,
+                                AccessibilityManager::getInstalledAccessibilityServiceList);
+
                 if (installedServices != null) {
                     for (AccessibilityServiceInfo service : installedServices) {
                         ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
@@ -11829,10 +11829,12 @@
                         }
                     }
                 }
+            } finally {
+                mInjector.binderRestoreCallingIdentity(id);
             }
-
-            return result;
         }
+
+        return result;
     }
 
     @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 8a5d3a6..63e8e56 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -50,6 +50,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -1078,7 +1079,7 @@
                 eq(getUidForPackage(PACKAGE_GREEN)), anyInt(), eq(Intent.ACTION_TIME_TICK),
                 eq(BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST),
                 eq(BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD),
-                anyLong(), anyLong(), anyLong(), anyInt()), times(1));
+                anyLong(), anyLong(), anyLong(), anyInt(), anyString(), anyString()), times(1));
     }
 
     private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 2d4f5ca..bca39ae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -1646,6 +1646,79 @@
     }
 
     /**
+     * Verify prioritized receivers work as expected with deferrable broadcast - broadcast to
+     * app in cached state should be deferred and the rest should be delivered as per the priority
+     * order.
+     */
+    @Test
+    public void testPrioritized_withDeferrableBroadcasts() throws Exception {
+        // Legacy stack doesn't support deferral
+        Assume.assumeTrue(mImpl == Impl.MODERN);
+
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+        final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+        final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+        final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
+        final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE);
+
+        receiverGreenApp.setCached(true);
+        receiverBlueApp.setCached(true);
+
+        final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+        final BroadcastOptions opts = BroadcastOptions.makeBasic()
+                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
+        final List receivers = List.of(
+                makeRegisteredReceiver(callerApp, 10),
+                makeRegisteredReceiver(receiverGreenApp, 9),
+                makeRegisteredReceiver(receiverBlueApp, 8),
+                makeRegisteredReceiver(receiverYellowApp, 8),
+                makeRegisteredReceiver(receiverOrangeApp, 7)
+        );
+        enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, opts, receivers));
+        waitForIdle();
+
+        // Green ignored since it's in cached state
+        verifyScheduleRegisteredReceiver(never(), receiverGreenApp, timeTick);
+        // Blue ignored since it's in cached state
+        verifyScheduleRegisteredReceiver(never(), receiverBlueApp, timeTick);
+
+        final IApplicationThread redThread = mAms.getProcessRecordLocked(PACKAGE_RED,
+                getUidForPackage(PACKAGE_RED)).getThread();
+        final IApplicationThread yellowThread = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+                getUidForPackage(PACKAGE_YELLOW)).getThread();
+        final IApplicationThread orangeThread = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
+                getUidForPackage(PACKAGE_ORANGE)).getThread();
+
+        // Verify apps that are not in cached state will receive the broadcast in the order
+        // we expect.
+        final InOrder inOrder = inOrder(redThread, yellowThread, orangeThread);
+        inOrder.verify(redThread).scheduleRegisteredReceiver(
+                any(), argThat(filterEqualsIgnoringComponent(timeTick)),
+                anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+                eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+        inOrder.verify(yellowThread).scheduleRegisteredReceiver(
+                any(), argThat(filterEqualsIgnoringComponent(timeTick)),
+                anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+                eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+        inOrder.verify(orangeThread).scheduleRegisteredReceiver(
+                any(), argThat(filterEqualsIgnoringComponent(timeTick)),
+                anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+                eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+
+        // Shift blue to be active and confirm that deferred broadcast is delivered
+        receiverBlueApp.setCached(false);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false);
+        waitForIdle();
+        verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
+
+        // Shift green to be active and confirm that deferred broadcast is delivered
+        receiverGreenApp.setCached(false);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
+        waitForIdle();
+        verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
+    }
+
+    /**
      * Verify that we handle replacing a pending broadcast.
      */
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
index 8f07238..3ad24de 100644
--- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
@@ -80,7 +80,9 @@
             asAdapter = mMockAudioSystem;
         }
         mSpatHelper = new SpatializerHelper(mMockAudioService, asAdapter,
-                false /*headTrackingEnabledByDefault*/);
+                true /*binauralEnabledDefault*/,
+                true /*transauralEnabledDefault*/,
+                false /*headTrackingEnabledDefault*/);
 
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 86878c53..8487903 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -232,7 +232,6 @@
         long mElapsedRealtime;
         boolean mIsAppIdleEnabled = true;
         boolean mIsCharging;
-        boolean mIsRestrictedBucketEnabled = true;
         List<String> mNonIdleWhitelistApps = new ArrayList<>();
         boolean mDisplayOn;
         DisplayManager.DisplayListener mDisplayListener;
@@ -316,11 +315,6 @@
         }
 
         @Override
-        boolean isRestrictedBucketEnabled() {
-            return mIsRestrictedBucketEnabled;
-        }
-
-        @Override
         File getDataSystemDirectory() {
             return new File(getContext().getFilesDir(), Long.toString(sRandom.nextLong()));
         }
@@ -1355,50 +1349,6 @@
 
     @Test
     @FlakyTest(bugId = 185169504)
-    public void testRestrictedBucketDisabled() throws Exception {
-        mInjector.mIsRestrictedBucketEnabled = false;
-        // Get the controller to read the new value. Capturing the ContentObserver isn't possible
-        // at the moment.
-        mController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
-
-        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
-        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
-
-        // Nothing should be able to put it into the RESTRICTED bucket.
-        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
-                REASON_MAIN_TIMEOUT);
-        assertNotBucket(STANDBY_BUCKET_RESTRICTED);
-        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
-                REASON_MAIN_PREDICTED);
-        assertNotBucket(STANDBY_BUCKET_RESTRICTED);
-        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
-                REASON_MAIN_FORCED_BY_SYSTEM);
-        assertNotBucket(STANDBY_BUCKET_RESTRICTED);
-        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
-                REASON_MAIN_FORCED_BY_USER);
-        assertNotBucket(STANDBY_BUCKET_RESTRICTED);
-    }
-
-    @Test
-    @FlakyTest(bugId = 185169504)
-    public void testRestrictedBucket_EnabledToDisabled() throws Exception {
-        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
-        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
-        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
-                REASON_MAIN_FORCED_BY_SYSTEM);
-        assertBucket(STANDBY_BUCKET_RESTRICTED);
-
-        mInjector.mIsRestrictedBucketEnabled = false;
-        // Get the controller to read the new value. Capturing the ContentObserver isn't possible
-        // at the moment.
-        mController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
-
-        mController.checkIdleStates(USER_ID);
-        assertNotBucket(STANDBY_BUCKET_RESTRICTED);
-    }
-
-    @Test
-    @FlakyTest(bugId = 185169504)
     public void testPredictionRaiseFromRestrictedTimeout_highBucket() throws Exception {
         reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 91d4f8f..d73a3b8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -15,9 +15,11 @@
  */
 package com.android.server.notification;
 
-import static org.hamcrest.Matchers.contains;
+import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.NO_SORT_BY_INTERRUPTIVENESS;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -43,13 +45,14 @@
 import android.telecom.TelecomManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import androidx.test.runner.AndroidJUnit4;
-
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.server.UiServiceTestCase;
 
+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.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -58,7 +61,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 public class NotificationComparatorTest extends UiServiceTestCase {
     @Mock Context mContext;
     @Mock TelecomManager mTm;
@@ -92,9 +95,24 @@
     private NotificationRecord mRecordColorized;
     private NotificationRecord mRecordColorizedCall;
 
+    @Parameterized.Parameters(name = "sortByInterruptiveness={0}")
+    public static Boolean[] getSortByInterruptiveness() {
+        return new Boolean[] { true, false };
+    }
+
+    @Parameterized.Parameter
+    public boolean mSortByInterruptiveness;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> {
+            if (flag.mSysPropKey.equals(NO_SORT_BY_INTERRUPTIVENESS.mSysPropKey)) {
+                return !mSortByInterruptiveness;
+            }
+            return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag);
+        };
+
         int userId = UserHandle.myUserId();
 
         when(mContext.getResources()).thenReturn(getContext().getResources());
@@ -126,7 +144,7 @@
                 new StatusBarNotification(callPkg,
                         callPkg, 1, "mRecordMinCallNonInterruptive", callUid, callUid,
                         nonInterruptiveNotif,
-                        new UserHandle(userId), "", 2000), getDefaultChannel());
+                        new UserHandle(userId), "", 2001), getDefaultChannel());
         mRecordMinCallNonInterruptive.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
         mRecordMinCallNonInterruptive.setInterruptive(false);
 
@@ -228,7 +246,7 @@
                 .setColorized(true).setColor(Color.WHITE)
                 .build();
         mRecordCheaterColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, 1, "cheater", uid2, uid2, n11, new UserHandle(userId),
+                pkg2, 1, "cheaterColorized", uid2, uid2, n11, new UserHandle(userId),
                 "", 9258), getDefaultChannel());
         mRecordCheaterColorized.setSystemImportance(NotificationManager.IMPORTANCE_LOW);
 
@@ -262,6 +280,11 @@
         mRecordColorizedCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
     }
 
+    @After
+    public void tearDown() {
+        SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
+    }
+
     @Test
     public void testOrdering() {
         final List<NotificationRecord> expected = new ArrayList<>();
@@ -281,8 +304,13 @@
         expected.add(mNoMediaSessionMedia);
         expected.add(mRecordCheater);
         expected.add(mRecordCheaterColorized);
-        expected.add(mRecordMinCall);
-        expected.add(mRecordMinCallNonInterruptive);
+        if (mSortByInterruptiveness) {
+            expected.add(mRecordMinCall);
+            expected.add(mRecordMinCallNonInterruptive);
+        } else {
+            expected.add(mRecordMinCallNonInterruptive);
+            expected.add(mRecordMinCall);
+        }
 
         List<NotificationRecord> actual = new ArrayList<>();
         actual.addAll(expected);
@@ -290,14 +318,18 @@
 
         Collections.sort(actual, new NotificationComparator(mContext));
 
-        assertThat(actual, contains(expected.toArray()));
+        assertThat(actual).containsExactlyElementsIn(expected).inOrder();
     }
 
     @Test
     public void testRankingScoreOverrides() {
         NotificationComparator comp = new NotificationComparator(mContext);
         NotificationRecord recordMinCallNonInterruptive = spy(mRecordMinCallNonInterruptive);
-        assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) < 0);
+        if (mSortByInterruptiveness) {
+            assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) < 0);
+        } else {
+            assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) > 0);
+        }
 
         when(recordMinCallNonInterruptive.getRankingScore()).thenReturn(1f);
         assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) > 0);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 39060cb..02c030d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5841,6 +5841,57 @@
     }
 
     @Test
+    public void testVisualDifference_sameImages() {
+        Icon large = Icon.createWithResource(mContext, 1);
+        Notification n1 = new Notification.Builder(mContext, "channel")
+                .setSmallIcon(1).setLargeIcon(large).build();
+        Notification n2 = new Notification.Builder(mContext, "channel")
+                .setSmallIcon(1).setLargeIcon(large).build();
+
+        NotificationRecord r1 = notificationToRecord(n1);
+        NotificationRecord r2 = notificationToRecord(n2);
+
+        assertThat(mService.isVisuallyInterruptive(r1, r2)).isFalse();
+    }
+
+    @Test
+    public void testVisualDifference_differentSmallImage() {
+        Icon large = Icon.createWithResource(mContext, 1);
+        Notification n1 = new Notification.Builder(mContext, "channel")
+                .setSmallIcon(1).setLargeIcon(large).build();
+        Notification n2 = new Notification.Builder(mContext, "channel")
+                .setSmallIcon(2).setLargeIcon(large).build();
+
+        NotificationRecord r1 = notificationToRecord(n1);
+        NotificationRecord r2 = notificationToRecord(n2);
+
+        assertThat(mService.isVisuallyInterruptive(r1, r2)).isTrue();
+    }
+
+    @Test
+    public void testVisualDifference_differentLargeImage() {
+        Icon large1 = Icon.createWithResource(mContext, 1);
+        Icon large2 = Icon.createWithResource(mContext, 2);
+        Notification n1 = new Notification.Builder(mContext, "channel")
+                .setSmallIcon(1).setLargeIcon(large1).build();
+        Notification n2 = new Notification.Builder(mContext, "channel")
+                .setSmallIcon(1).setLargeIcon(large2).build();
+
+        NotificationRecord r1 = notificationToRecord(n1);
+        NotificationRecord r2 = notificationToRecord(n2);
+
+        assertThat(mService.isVisuallyInterruptive(r1, r2)).isTrue();
+    }
+
+    private NotificationRecord notificationToRecord(Notification n) {
+        return new NotificationRecord(
+                mContext,
+                new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0, n,
+                        UserHandle.getUserHandleForUid(mUid), null, 0),
+                mock(NotificationChannel.class));
+    }
+
+    @Test
     public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
         // post 2 notification from this package
         final NotificationRecord notif1 = generateNotificationRecord(
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 2cc46a9..adc3db7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -108,7 +108,6 @@
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -604,11 +603,11 @@
         // The scale is 2000/2500=0.8. The horizontal centered offset is (1000-(1000*0.8))/2=100.
         final float scale = (float) display.mBaseDisplayHeight / currentBounds.height();
         final int offsetX = (int) (display.mBaseDisplayWidth - (origBounds.width() * scale)) / 2;
-        assertEquals(offsetX, currentBounds.left);
+        final int screenX = mActivity.getBounds().left;
+        assertEquals(offsetX, screenX);
 
-        // The position of configuration bounds should be the same as compat bounds.
-        assertEquals(mActivity.getBounds().left, currentBounds.left);
-        assertEquals(mActivity.getBounds().top, currentBounds.top);
+        // The position of configuration bounds should be in app space.
+        assertEquals(screenX, (int) (currentBounds.left * scale + 0.5f));
         // Activity is sandboxed to the offset size compat bounds.
         assertActivityMaxBoundsSandboxed();
 
@@ -638,7 +637,7 @@
         // The size should still be in portrait [100, 0 - 1100, 2500] = 1000x2500.
         assertEquals(origBounds.width(), currentBounds.width());
         assertEquals(origBounds.height(), currentBounds.height());
-        assertEquals(offsetX, currentBounds.left);
+        assertEquals(offsetX, mActivity.getBounds().left);
         assertScaled();
         // Activity is sandboxed due to size compat mode.
         assertActivityMaxBoundsSandboxed();
@@ -801,9 +800,11 @@
         assertEquals(origAppBounds.height(), appBounds.height());
         // The activity is 1000x1400 and the display is 2500x1000.
         assertScaled();
-        // The position in configuration should be global coordinates.
-        assertEquals(mActivity.getBounds().left, currentBounds.left);
-        assertEquals(mActivity.getBounds().top, currentBounds.top);
+        final float scale = mActivity.getCompatScale();
+        // The position in configuration should be in app coordinates.
+        final Rect screenBounds = mActivity.getBounds();
+        assertEquals(screenBounds.left, (int) (currentBounds.left * scale + 0.5f));
+        assertEquals(screenBounds.top, (int) (currentBounds.top * scale + 0.5f));
 
         // Activity max bounds are sandboxed due to size compat mode.
         assertActivityMaxBoundsSandboxed();
@@ -2025,7 +2026,7 @@
         float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
         final Rect afterBounds = activity.getBounds();
         final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
-        Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
     }
 
     @Test
@@ -2050,7 +2051,7 @@
         float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
         final Rect afterBounds = activity.getBounds();
         final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
-        Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
     }
 
     @Test
@@ -2076,7 +2077,7 @@
         float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
         final Rect afterBounds = activity.getBounds();
         final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
-        Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
     }
 
     @Test
@@ -2102,7 +2103,89 @@
         float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
         final Rect afterBounds = activity.getBounds();
         final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
-        Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+    public void testOverrideSplitScreenAspectRatio_splitScreenActivityInPortrait_notLetterboxed() {
+        mAtm.mDevEnableNonResizableMultiWindow = true;
+        final int screenWidth = 1800;
+        final int screenHeight = 1000;
+        setUpDisplaySizeWithApp(screenWidth, screenHeight);
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Simulate real display with top insets.
+        final int topInset = 30;
+        activity.mDisplayContent.getWindowConfiguration()
+                .setAppBounds(0, topInset, screenWidth, screenHeight);
+
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, activity.getDisplayContent());
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+        organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(screenWidth), screenHeight);
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
+
+        // Unresizable portrait-only activity.
+        prepareUnresizable(activity, 3f, SCREEN_ORIENTATION_PORTRAIT);
+
+        // Activity should have the aspect ratio of a split screen activity and occupy exactly one
+        // half of the screen, so there is no letterbox
+        float expectedAspectRatio = 1f * screenHeight / getExpectedSplitSize(screenWidth);
+        final Rect afterBounds = activity.getBounds();
+        final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
+        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+        assertFalse(activity.areBoundsLetterboxed());
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+    public void testOverrideSplitScreenAspectRatio_splitScreenActivityInLandscape_notLetterboxed() {
+        mAtm.mDevEnableNonResizableMultiWindow = true;
+        final int screenWidth = 1000;
+        final int screenHeight = 1800;
+        setUpDisplaySizeWithApp(screenWidth, screenHeight);
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Simulate real display with top insets.
+        final int leftInset = 30;
+        activity.mDisplayContent.getWindowConfiguration()
+                .setAppBounds(leftInset, 0, screenWidth, screenHeight);
+
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, activity.getDisplayContent());
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+        organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
+
+        // Unresizable landscape-only activity.
+        prepareUnresizable(activity, 3f, SCREEN_ORIENTATION_LANDSCAPE);
+
+        // Activity should have the aspect ratio of a split screen activity and occupy exactly one
+        // half of the screen, so there is no letterbox
+        float expectedAspectRatio = 1f * screenWidth / getExpectedSplitSize(screenHeight);
+        final Rect afterBounds = activity.getBounds();
+        final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
+        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+        assertFalse(activity.areBoundsLetterboxed());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 1d0715a..2a2641e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -314,7 +314,7 @@
     }
 
     @Override
-    public int applyKeyguardOcclusionChange(boolean notify) {
+    public int applyKeyguardOcclusionChange() {
         return 0;
     }
 
diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java
index 7c794473..e8a8576 100644
--- a/telephony/java/android/telephony/satellite/PointingInfo.java
+++ b/telephony/java/android/telephony/satellite/PointingInfo.java
@@ -17,6 +17,7 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -33,6 +34,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public PointingInfo(float satelliteAzimuthDegrees, float satelliteElevationDegrees) {
         mSatelliteAzimuthDegrees = satelliteAzimuthDegrees;
         mSatelliteElevationDegrees = satelliteElevationDegrees;
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
index df80159..87c8db3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
@@ -17,6 +17,7 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -45,6 +46,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public SatelliteCapabilities(Set<Integer> supportedRadioTechnologies,
             boolean isPointingRequired, int maxBytesPerOutgoingDatagram) {
         mSupportedRadioTechnologies = supportedRadioTechnologies == null
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagram.java b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
index d3cb8a0..44f56f3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagram.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
@@ -17,6 +17,7 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,6 +33,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public SatelliteDatagram(@NonNull byte[] data) {
         mData = data;
     }
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index f237ada..d8a6faf 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.telephony.ILongConsumer;
 
@@ -36,6 +37,7 @@
      *                 datagramId to Telephony. If the callback is not received within five minutes,
      *                 Telephony will resend the datagram.
      */
+    @UnsupportedAppUsage
     void onSatelliteDatagramReceived(long datagramId, @NonNull SatelliteDatagram datagram,
             int pendingCount, @NonNull ILongConsumer callback);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index e32566d..7d82fd8 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -82,6 +83,7 @@
      * @param context The context the SatelliteManager belongs to.
      * @hide
      */
+    @UnsupportedAppUsage
     public SatelliteManager(@Nullable Context context) {
         this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
     }
@@ -127,6 +129,7 @@
      * {@link #requestIsSatelliteEnabled(Executor, OutcomeReceiver)}.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String KEY_SATELLITE_ENABLED = "satellite_enabled";
 
     /**
@@ -134,6 +137,7 @@
      * {@link #requestIsDemoModeEnabled(Executor, OutcomeReceiver)}.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String KEY_DEMO_MODE_ENABLED = "demo_mode_enabled";
 
     /**
@@ -141,6 +145,7 @@
      * {@link #requestIsSatelliteSupported(Executor, OutcomeReceiver)}.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String KEY_SATELLITE_SUPPORTED = "satellite_supported";
 
     /**
@@ -148,6 +153,7 @@
      * {@link #requestSatelliteCapabilities(Executor, OutcomeReceiver)}.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String KEY_SATELLITE_CAPABILITIES = "satellite_capabilities";
 
     /**
@@ -155,6 +161,7 @@
      * {@link #requestIsSatelliteProvisioned(Executor, OutcomeReceiver)}.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String KEY_SATELLITE_PROVISIONED = "satellite_provisioned";
 
     /**
@@ -162,6 +169,7 @@
      * {@link #requestIsSatelliteCommunicationAllowedForCurrentLocation(Executor, OutcomeReceiver)}.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String KEY_SATELLITE_COMMUNICATION_ALLOWED =
             "satellite_communication_allowed";
 
@@ -170,6 +178,7 @@
      * {@link #requestTimeForNextSatelliteVisibility(Executor, OutcomeReceiver)}.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
 
     /**
@@ -340,6 +349,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
             @NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener) {
@@ -382,6 +392,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void requestIsSatelliteEnabled(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -436,6 +447,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void requestIsDemoModeEnabled(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -488,6 +500,7 @@
      *
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
+    @UnsupportedAppUsage
     public void requestIsSatelliteSupported(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -541,6 +554,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void requestSatelliteCapabilities(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<SatelliteCapabilities, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -707,6 +721,7 @@
      */
     public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2;
 
+    /** @hide */
     @IntDef(prefix = "DATAGRAM_TYPE_", value = {
             DATAGRAM_TYPE_UNKNOWN,
             DATAGRAM_TYPE_SOS_MESSAGE,
@@ -732,6 +747,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void startSatelliteTransmissionUpdates(@NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener,
             @NonNull SatelliteTransmissionUpdateCallback callback) {
@@ -801,6 +817,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void stopSatelliteTransmissionUpdates(
             @NonNull SatelliteTransmissionUpdateCallback callback,
             @NonNull @CallbackExecutor Executor executor,
@@ -856,6 +873,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void provisionSatelliteService(@NonNull String token, @NonNull String regionId,
             @Nullable CancellationSignal cancellationSignal,
             @NonNull @CallbackExecutor Executor executor,
@@ -895,7 +913,7 @@
      * {@link SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean)}
      * should report as deprovisioned.
      * For provisioning satellite service, refer to
-     * {@link #provisionSatelliteService(String, CancellationSignal, Executor, Consumer)}.
+     * {@link #provisionSatelliteService(String, String, CancellationSignal, Executor, Consumer)}
      *
      * @param token The token of the device/subscription to be deprovisioned.
      * @param resultListener Listener for the {@link SatelliteError} result of the operation.
@@ -904,6 +922,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void deprovisionSatelliteService(@NonNull String token,
             @NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener) {
@@ -943,6 +962,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     @SatelliteError public int registerForSatelliteProvisionStateChanged(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull SatelliteProvisionStateCallback callback) {
@@ -985,6 +1005,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void unregisterForSatelliteProvisionStateChanged(
             @NonNull SatelliteProvisionStateCallback callback) {
         Objects.requireNonNull(callback);
@@ -1023,6 +1044,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void requestIsSatelliteProvisioned(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -1074,6 +1096,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     @SatelliteError public int registerForSatelliteModemStateChanged(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull SatelliteStateCallback callback) {
@@ -1113,6 +1136,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) {
         Objects.requireNonNull(callback);
         ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback);
@@ -1147,6 +1171,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     @SatelliteError public int registerForSatelliteDatagram(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull SatelliteDatagramCallback callback) {
@@ -1190,6 +1215,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void unregisterForSatelliteDatagram(@NonNull SatelliteDatagramCallback callback) {
         Objects.requireNonNull(callback);
         ISatelliteDatagramCallback internalCallback =
@@ -1227,6 +1253,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void pollPendingSatelliteDatagrams(@NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener) {
         Objects.requireNonNull(executor);
@@ -1279,6 +1306,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void sendSatelliteDatagram(@DatagramType int datagramType,
             @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
             @NonNull @CallbackExecutor Executor executor,
@@ -1324,6 +1352,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
@@ -1381,6 +1410,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @UnsupportedAppUsage
     public void requestTimeForNextSatelliteVisibility(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Duration, SatelliteException> callback) {
         Objects.requireNonNull(executor);
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index a62eb8b..20875af 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -16,6 +16,8 @@
 
 package android.telephony.satellite;
 
+import android.compat.annotation.UnsupportedAppUsage;
+
 /**
  * A callback class for monitoring satellite provision state change events.
  *
@@ -28,5 +30,6 @@
      * @param provisioned The new provision state. {@code true} means satellite is provisioned
      *                    {@code false} means satellite is not provisioned.
      */
+    @UnsupportedAppUsage
     void onSatelliteProvisionStateChanged(boolean provisioned);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
index d9ecaa3..3312e3a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
@@ -16,6 +16,8 @@
 
 package android.telephony.satellite;
 
+import android.compat.annotation.UnsupportedAppUsage;
+
 /**
  * A callback class for monitoring satellite modem state change events.
  *
@@ -26,5 +28,6 @@
      * Called when satellite modem state changes.
      * @param state The new satellite modem state.
      */
+    @UnsupportedAppUsage
     void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index d4fe57a..17fff44 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A callback class for monitoring satellite position update and datagram transfer state change
@@ -30,6 +31,7 @@
      *
      * @param pointingInfo The pointing info containing the satellite location.
      */
+    @UnsupportedAppUsage
     void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo);
 
     /**
@@ -39,6 +41,7 @@
      * @param sendPendingCount The number of datagrams that are currently being sent.
      * @param errorCode If datagram transfer failed, the reason for failure.
      */
+    @UnsupportedAppUsage
     void onSendDatagramStateChanged(
             @SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
             @SatelliteManager.SatelliteError int errorCode);
@@ -50,6 +53,7 @@
      * @param receivePendingCount The number of datagrams that are currently pending to be received.
      * @param errorCode If datagram transfer failed, the reason for failure.
      */
+    @UnsupportedAppUsage
     void onReceiveDatagramStateChanged(
             @SatelliteManager.SatelliteDatagramTransferState int state, int receivePendingCount,
             @SatelliteManager.SatelliteError int errorCode);
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 5dc2dd7..47b2cda 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
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.close
 
 import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -40,20 +39,27 @@
  *     Launch an app [testApp] and wait animation to complete
  *     Press back button
  * ```
+ *
  * To run only the presubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
  * ```
+ *
  * To run only the postsubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
  * ```
+ *
  * To run only the flaky assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
@@ -65,7 +71,6 @@
  * ```
  */
 @RequiresDevice
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
index 9fa84019..70eedd9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.close
 
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -25,7 +24,6 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
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 b042a14..d8abb4e 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
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.close
 
 import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -40,20 +39,27 @@
  *     Launch an app [testApp] and wait animation to complete
  *     Press home button
  * ```
+ *
  * To run only the presubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
  * ```
+ *
  * To run only the postsubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
  * ```
+ *
  * To run only the flaky assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
@@ -65,7 +71,6 @@
  * ```
  */
 @RequiresDevice
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
index 136995a..c74f54b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.close
 
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -25,7 +24,6 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
index 3289bc6..ac05c76 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
@@ -41,4 +41,4 @@
             return FlickerTestFactory.nonRotationTests()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
index ccbe74f..09c17b1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
@@ -18,7 +18,6 @@
 
 import android.platform.test.annotations.FlakyTest
 import android.tools.common.NavBar
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -29,7 +28,6 @@
 import org.junit.runners.Parameterized
 
 /** Some assertions will fail because of b/264415996 */
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
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 d0dc42f..5cacb04 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
@@ -18,14 +18,13 @@
 
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.helpers.setRotation
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -42,6 +41,7 @@
  *     Make sure no apps are running on the device
  *     Launch an app [testApp] and wait animation to complete
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
@@ -53,7 +53,6 @@
  * ```
  */
 @RequiresDevice
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
index f75d9ee..f77f968 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.launch
 
 import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -27,7 +26,6 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,4 +47,4 @@
             return FlickerTestFactory.nonRotationTests()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
index 4aa78d4..8b4a613 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
@@ -44,4 +44,4 @@
             return FlickerTestFactory.nonRotationTests()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
new file mode 100644
index 0000000..d90b3ca
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppFromNotificationWarmCfArm(flicker: FlickerTest) :
+    OpenAppFromNotificationWarm(flicker) {
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+         * navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<FlickerTest> {
+            return FlickerTestFactory.nonRotationTests()
+        }
+    }
+}
\ No newline at end of file
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 00d7544..66e0f06 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
@@ -19,7 +19,6 @@
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -44,6 +43,7 @@
  *     Relaunch an app [testApp] by selecting it in the overview screen, and wait animation to
  *     complete (only this action is traced)
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
@@ -55,7 +55,6 @@
  * ```
  */
 @RequiresDevice
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
index ff24190..8139e1f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -26,7 +25,6 @@
 import org.junit.runners.Parameterized
 
 /** Some assertions will fail because of b/264415996 */
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 9ab6156..14df84e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -22,7 +22,6 @@
 import android.tools.common.NavBar
 import android.tools.common.Rotation
 import android.tools.common.datatypes.component.ComponentNameMatcher
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -48,6 +47,7 @@
  *     Lock the device.
  *     Launch an app on top of the lock screen [testApp] and wait animation to complete
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
@@ -59,7 +59,6 @@
  * ```
  */
 @RequiresDevice
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
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 cdd2d45..cfc8e46 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
@@ -18,7 +18,6 @@
 
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -42,6 +41,7 @@
  *     Press home
  *     Relaunch an app [testApp] and wait animation to complete (only this action is traced)
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
@@ -53,7 +53,6 @@
  * ```
  */
 @RequiresDevice
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
index 9679059..b47c931 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -25,7 +24,6 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -43,4 +41,4 @@
             return FlickerTestFactory.nonRotationTests()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
index 786bb32..e876e57 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -24,10 +24,10 @@
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
 import android.view.KeyEvent
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.helpers.setRotation
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,6 +44,7 @@
  *     Make sure no apps are running on the device
  *     Launch an app [testApp] and wait animation to complete
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
index b848e63..6cbb975 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -27,13 +27,13 @@
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.R
-import com.android.server.wm.flicker.helpers.setRotation
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
-import android.tools.device.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING
index 94e4f4d..14f5af3 100644
--- a/wifi/TEST_MAPPING
+++ b/wifi/TEST_MAPPING
@@ -3,5 +3,15 @@
     {
       "name": "FrameworksWifiNonUpdatableApiTests"
     }
+  ],
+  "presubmit-large": [
+    {
+      "name": "CtsWifiTestCases",
+      "options": [
+        {
+          "exclude-annotation": "android.net.wifi.cts.VirtualDeviceNotSupported"
+        }
+      ]
+    }
   ]
 }