Merge "Support gainmap HW copy." into udc-dev
diff --git a/Android.bp b/Android.bp
index effd7ce..d961599 100644
--- a/Android.bp
+++ b/Android.bp
@@ -580,7 +580,7 @@
     "--hide Todo " +
     "--hide Typo " +
     "--hide UnavailableSymbol " +
-    "--manifest $(location core/res/AndroidManifest.xml) "
+    "--manifest $(location :frameworks-base-core-AndroidManifest.xml) "
 
 packages_to_document = [
     "android",
@@ -617,7 +617,7 @@
     sdk_version: "none",
     system_modules: "none",
     java_version: "1.8",
-    arg_files: ["core/res/AndroidManifest.xml"],
+    arg_files: [":frameworks-base-core-AndroidManifest.xml"],
     aidl: {
         local_include_dirs: [
             "media/aidl",
@@ -696,12 +696,3 @@
     "ProtoLibraries.bp",
     "TestProtoLibraries.bp",
 ]
-
-java_api_contribution {
-    name: "api-stubs-docs-non-updatable-public-stubs",
-    api_surface: "public",
-    api_file: "core/api/current.txt",
-    visibility: [
-        "//build/orchestrator/apis",
-    ],
-}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 38413c2..b005591 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -36,8 +36,8 @@
     args: metalava_framework_docs_args,
     check_api: {
         current: {
-            api_file: "core/api/current.txt",
-            removed_api_file: "core/api/removed.txt",
+            api_file: ":non-updatable-current.txt",
+            removed_api_file: ":non-updatable-removed.txt",
         },
         last_released: {
             api_file: ":android-non-updatable.api.public.latest",
@@ -88,8 +88,8 @@
     args: metalava_framework_docs_args + priv_apps,
     check_api: {
         current: {
-            api_file: "core/api/system-current.txt",
-            removed_api_file: "core/api/system-removed.txt",
+            api_file: ":non-updatable-system-current.txt",
+            removed_api_file: ":non-updatable-system-removed.txt",
         },
         last_released: {
             api_file: ":android-non-updatable.api.system.latest",
@@ -99,7 +99,7 @@
         api_lint: {
             enabled: true,
             new_since: ":android.api.system.latest",
-            baseline_file: "core/api/system-lint-baseline.txt",
+            baseline_file: ":non-updatable-system-lint-baseline.txt",
         },
     },
     dists: [
@@ -127,12 +127,12 @@
     args: metalava_framework_docs_args + test + priv_apps_in_stubs,
     check_api: {
         current: {
-            api_file: "core/api/test-current.txt",
-            removed_api_file: "core/api/test-removed.txt",
+            api_file: ":non-updatable-test-current.txt",
+            removed_api_file: ":non-updatable-test-removed.txt",
         },
         api_lint: {
             enabled: true,
-            baseline_file: "core/api/test-lint-baseline.txt",
+            baseline_file: ":non-updatable-test-lint-baseline.txt",
         },
     },
     dists: [
@@ -172,8 +172,8 @@
     args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs,
     check_api: {
         current: {
-            api_file: "core/api/module-lib-current.txt",
-            removed_api_file: "core/api/module-lib-removed.txt",
+            api_file: ":non-updatable-module-lib-current.txt",
+            removed_api_file: ":non-updatable-module-lib-removed.txt",
         },
         last_released: {
             api_file: ":android-non-updatable.api.module-lib.latest",
@@ -183,7 +183,7 @@
         api_lint: {
             enabled: true,
             new_since: ":android.api.module-lib.latest",
-            baseline_file: "core/api/module-lib-lint-baseline.txt",
+            baseline_file: ":non-updatable-module-lib-lint-baseline.txt",
         },
     },
     dists: [
@@ -364,15 +364,15 @@
 
 java_library {
     name: "android_system_server_stubs_current",
-    defaults: ["android_stubs_dists_default"],
+    defaults: [
+        "android.jar_defaults",
+        "android_stubs_dists_default",
+    ],
     srcs: [":services-non-updatable-stubs"],
     installable: false,
     static_libs: [
         "android_module_lib_stubs_current",
     ],
-    sdk_version: "none",
-    system_modules: "none",
-    java_version: "1.8",
     dist: {
         dir: "apistubs/android/system-server",
     },
@@ -575,20 +575,7 @@
 
 droidstubs {
     name: "hwbinder-stubs-docs",
-    srcs: [
-        "core/java/android/os/HidlSupport.java",
-        "core/java/android/os/HidlMemory.java",
-        "core/java/android/os/HwBinder.java",
-        "core/java/android/os/HwBlob.java",
-        "core/java/android/os/HwParcel.java",
-        "core/java/android/os/IHwBinder.java",
-        "core/java/android/os/IHwInterface.java",
-        "core/java/android/os/DeadObjectException.java",
-        "core/java/android/os/DeadSystemException.java",
-        "core/java/android/os/NativeHandle.java",
-        "core/java/android/os/RemoteException.java",
-        "core/java/android/util/AndroidException.java",
-    ],
+    srcs: [":hwbinder-stubs-srcs"],
     libs: ["framework-annotations-lib"],
     installable: false,
     sdk_version: "core_platform",
@@ -610,12 +597,3 @@
     ],
     visibility: ["//visibility:public"],
 }
-
-java_api_contribution {
-    name: "frameworks-base-core-api-module-lib-stubs",
-    api_surface: "module-lib",
-    api_file: "core/api/module-lib-current.txt",
-    visibility: [
-        "//build/orchestrator/apis",
-    ],
-}
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
index 2e44d82..e9c6c1a 100644
--- a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
+++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
@@ -76,6 +76,7 @@
         implements ManualBenchmarkState.CustomizedIterationListener {
     private static final String TAG = ImePerfTest.class.getSimpleName();
     private static final long ANIMATION_NOT_STARTED = -1;
+    private static final int WAIT_PROCESS_KILL_TIMEOUT_MS = 2000;
 
     @Rule
     public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
@@ -248,19 +249,18 @@
         boolean shouldRetry = false;
         while (shouldRetry || state.keepRunning(measuredTimeNs)) {
             shouldRetry = false;
-            killBaselineIme();
+            killBaselineImeSync();
             try (ImeSession imeSession = new ImeSession(BaselineIme.getName(
                     getInstrumentation().getContext()))) {
+                if (!mIsTraceStarted) {
+                    startAsyncAtrace();
+                }
                 final AtomicReference<CountDownLatch> latchStart = new AtomicReference<>();
                 final Activity activity = getActivityWithFocus();
 
                 setImeListener(activity, latchStart, null /* latchEnd */);
                 latchStart.set(new CountDownLatch(1));
 
-                if (!mIsTraceStarted) {
-                    startAsyncAtrace();
-                }
-
                 final WindowInsetsController controller =
                         activity.getWindow().getDecorView().getWindowInsetsController();
                 AtomicLong startTime = new AtomicLong();
@@ -270,6 +270,7 @@
                 });
 
                 measuredTimeNs = waitForAnimationStart(latchStart, startTime);
+                stopAsyncAtraceAndDumpTraces();
 
                 if (measuredTimeNs == ANIMATION_NOT_STARTED) {
                     // Animation didn't start within timeout,
@@ -285,7 +286,7 @@
         addResultToState(state);
     }
 
-    private void killBaselineIme() {
+    private void killBaselineImeSync() {
         // pidof returns a space separated list of numeric PIDs.
         String result = SystemUtil.runShellCommand(
                 "pidof com.android.perftests.inputmethod:BaselineIME");
@@ -294,7 +295,13 @@
             if (TextUtils.isEmpty(pid)) {
                 continue;
             }
-            Process.killProcess(Integer.parseInt(pid));
+            final int pidToKill = Integer.parseInt(pid);
+            Process.killProcess(pidToKill);
+            try {
+                // Wait kill IME process being settled down.
+                Process.waitForProcessDeath(pidToKill, WAIT_PROCESS_KILL_TIMEOUT_MS);
+            } catch (Exception e) {
+            }
         }
     }
 
@@ -381,7 +388,7 @@
             }
         } finally {
             if (mIsTraceStarted) {
-                stopAsyncAtrace();
+                stopAsyncAtraceAndDumpTraces();
             }
         }
         mActivityRule.finishActivity();
@@ -488,7 +495,7 @@
         startAsyncAtrace("wm view");
     }
 
-    private void stopAsyncAtrace() {
+    private void stopAsyncAtraceAndDumpTraces() {
         if (!mIsTraceStarted) {
             return;
         }
@@ -504,6 +511,14 @@
         }
     }
 
+    private void stopAsyncAtrace() {
+        if (!mIsTraceStarted) {
+            return;
+        }
+        mIsTraceStarted = false;
+        getUiAutomation().executeShellCommand("atrace --async_stop");
+    }
+
     @Override
     public void onStart(int iteration) {
         // Do not capture trace when profiling because the result will be much slower.
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfTestBase.java b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfTestBase.java
index ca59137..804baf4 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfTestBase.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfTestBase.java
@@ -73,7 +73,7 @@
     }
 
     public static void startAsyncAtrace(String tags) {
-        getUiAutomation().executeShellCommand("atrace -b 32768 --async_start " + tags);
+        getUiAutomation().executeShellCommand("atrace --async_start -b 32768 -c " + tags);
         // Avoid atrace isn't ready immediately.
         SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
     }
diff --git a/api/Android.bp b/api/Android.bp
index 78ddc6a..73dbd28 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -31,10 +31,12 @@
         "blueprint",
         "soong",
         "soong-android",
+        "soong-bp2build",
         "soong-genrule",
         "soong-java",
     ],
     srcs: ["api.go"],
+    testSrcs: ["api_test.go"],
     pluginFor: ["soong_build"],
 }
 
@@ -261,3 +263,15 @@
     out: ["combined-removed-dex.txt"],
     cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)",
 }
+
+java_genrule {
+    name: "api_fingerprint",
+    srcs: [
+        ":frameworks-base-api-current.txt",
+        ":frameworks-base-api-system-current.txt",
+        ":frameworks-base-api-module-lib-current.txt",
+        ":frameworks-base-api-system-server-current.txt",
+    ],
+    out: ["api_fingerprint.txt"],
+    cmd: "cat $(in) | md5sum | cut -d' ' -f1 > $(out)",
+}
diff --git a/api/api.go b/api/api.go
index 077ab96..25d9728 100644
--- a/api/api.go
+++ b/api/api.go
@@ -20,6 +20,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/genrule"
 	"android/soong/java"
 )
@@ -30,6 +31,7 @@
 const virtualization = "framework-virtualization"
 
 var core_libraries_modules = []string{art, conscrypt, i18n}
+
 // List of modules that are not yet updatable, and hence they can still compile
 // against hidden APIs. These modules are filtered out when building the
 // updatable-framework-module-impl (because updatable-framework-module-impl is
@@ -59,6 +61,7 @@
 
 type CombinedApis struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	properties CombinedApisProperties
 }
@@ -99,6 +102,19 @@
 	Visibility []string
 }
 
+type Bazel_module struct {
+	Bp2build_available *bool
+}
+type bazelProperties struct {
+	*Bazel_module
+}
+
+var bp2buildNotAvailable = bazelProperties{
+	&Bazel_module{
+		Bp2build_available: proptools.BoolPtr(false),
+	},
+}
+
 // Struct to pass parameters for the various merged [current|removed].txt file modules we create.
 type MergedTxtDefinition struct {
 	// "current.txt" or "removed.txt"
@@ -144,7 +160,7 @@
 		},
 	}
 	props.Visibility = []string{"//visibility:public"}
-	ctx.CreateModule(genrule.GenRuleFactory, &props)
+	ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable)
 }
 
 func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
@@ -174,7 +190,7 @@
 		props := fgProps{}
 		props.Name = proptools.StringPtr(i.name)
 		props.Srcs = createSrcs(i.modules, i.tag)
-		ctx.CreateModule(android.FileGroupFactory, &props)
+		ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable)
 	}
 }
 
@@ -223,7 +239,7 @@
 		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)
+		ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable)
 	}
 }
 
@@ -315,7 +331,7 @@
 	props.Name = proptools.StringPtr("all-modules-public-stubs-source")
 	props.Srcs = createSrcs(modules, "{.public.stubs.source}")
 	props.Visibility = []string{"//frameworks/base"}
-	ctx.CreateModule(android.FileGroupFactory, &props)
+	ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable)
 }
 
 func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
@@ -389,9 +405,57 @@
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
+	android.InitBazelModule(module)
 	return module
 }
 
+type bazelCombinedApisAttributes struct {
+	Scope bazel.StringAttribute
+	Base  bazel.LabelAttribute
+	Deps  bazel.LabelListAttribute
+}
+
+// combined_apis bp2build converter
+func (a *CombinedApis) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	basePrefix := "non-updatable"
+	scopeNames := []string{"public", "system", "module-lib", "system-server"}
+	scopeToSuffix := map[string]string{
+		"public":        "-current.txt",
+		"system":        "-system-current.txt",
+		"module-lib":    "-module-lib-current.txt",
+		"system-server": "-system-server-current.txt",
+	}
+
+	for _, scopeName := range scopeNames{
+		suffix := scopeToSuffix[scopeName]
+		name := a.Name() + suffix
+
+		var scope bazel.StringAttribute
+		scope.SetValue(scopeName)
+
+		var base bazel.LabelAttribute
+		base.SetValue(android.BazelLabelForModuleDepSingle(ctx, basePrefix+suffix))
+
+		var deps bazel.LabelListAttribute
+		classpath := a.properties.Bootclasspath
+		if scopeName == "system-server" {
+			classpath = a.properties.System_server_classpath
+		}
+		deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, classpath))
+
+		attrs := bazelCombinedApisAttributes{
+			Scope: scope,
+			Base:  base,
+			Deps:  deps,
+		}
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class:        "merged_txts",
+			Bzl_load_location: "//build/bazel/rules/java:merged_txts.bzl",
+		}
+		ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, &attrs)
+	}
+}
+
 // Various utility methods below.
 
 // Creates an array of ":<m><tag>" for each m in <modules>.
diff --git a/api/api_test.go b/api/api_test.go
new file mode 100644
index 0000000..15b695c
--- /dev/null
+++ b/api/api_test.go
@@ -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 api
+
+import (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/bp2build"
+)
+
+func runCombinedApisTestCaseWithRegistrationCtxFunc(t *testing.T, tc bp2build.Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "combined_apis"
+	(&tc).ModuleTypeUnderTestFactory = combinedApisModuleFactory
+	bp2build.RunBp2BuildTestCase(t, registrationCtxFunc, tc)
+}
+
+func runCombinedApisTestCase(t *testing.T, tc bp2build.Bp2buildTestCase) {
+	t.Helper()
+	runCombinedApisTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {})
+}
+
+func TestCombinedApisGeneral(t *testing.T) {
+	runCombinedApisTestCase(t, bp2build.Bp2buildTestCase{
+		Description: "combined_apis, general case",
+		Blueprint: `combined_apis {
+    name: "foo",
+    bootclasspath: ["bcp"],
+    system_server_classpath: ["ssc"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-current.txt", bp2build.AttrNameToString{
+				"scope": `"public"`,
+				"base":  `":non-updatable-current.txt__BP2BUILD__MISSING__DEP"`,
+				"deps":  `[":bcp__BP2BUILD__MISSING__DEP"]`,
+			}),
+			bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-current.txt", bp2build.AttrNameToString{
+				"scope": `"system"`,
+				"base":  `":non-updatable-system-current.txt__BP2BUILD__MISSING__DEP"`,
+				"deps":  `[":bcp__BP2BUILD__MISSING__DEP"]`,
+			}),
+			bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-module-lib-current.txt", bp2build.AttrNameToString{
+				"scope": `"module-lib"`,
+				"base":  `":non-updatable-module-lib-current.txt__BP2BUILD__MISSING__DEP"`,
+				"deps":  `[":bcp__BP2BUILD__MISSING__DEP"]`,
+			}),
+			bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-server-current.txt", bp2build.AttrNameToString{
+				"scope": `"system-server"`,
+				"base":  `":non-updatable-system-server-current.txt__BP2BUILD__MISSING__DEP"`,
+				"deps":  `[":ssc__BP2BUILD__MISSING__DEP"]`,
+			}),
+		},
+	})
+}
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 114a957..71a2ca2 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -13,7 +13,10 @@
 // limitations under the License.
 
 package {
-    default_visibility: ["//visibility:private"],
+    default_visibility: [
+        "//frameworks/base",
+        "//frameworks/base/api",
+    ],
     // 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"
@@ -27,31 +30,33 @@
 filegroup {
     name: "non-updatable-current.txt",
     srcs: ["current.txt"],
-    visibility: ["//frameworks/base/api"],
 }
 
 filegroup {
     name: "non-updatable-removed.txt",
     srcs: ["removed.txt"],
-    visibility: ["//frameworks/base/api"],
 }
 
 filegroup {
     name: "non-updatable-system-current.txt",
     srcs: ["system-current.txt"],
-    visibility: ["//frameworks/base/api"],
 }
 
 filegroup {
     name: "non-updatable-system-removed.txt",
     srcs: ["system-removed.txt"],
-    visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
+    name: "non-updatable-system-lint-baseline.txt",
+    srcs: ["system-lint-baseline.txt"],
 }
 
 filegroup {
     name: "non-updatable-module-lib-current.txt",
     srcs: ["module-lib-current.txt"],
     visibility: [
+        "//frameworks/base",
         "//frameworks/base/api",
         "//cts/tests/signature/api",
     ],
@@ -61,7 +66,46 @@
     name: "non-updatable-module-lib-removed.txt",
     srcs: ["module-lib-removed.txt"],
     visibility: [
+        "//frameworks/base",
         "//frameworks/base/api",
         "//cts/tests/signature/api",
     ],
 }
+
+filegroup {
+    name: "non-updatable-module-lib-lint-baseline.txt",
+    srcs: ["module-lib-lint-baseline.txt"],
+}
+
+filegroup {
+    name: "non-updatable-test-current.txt",
+    srcs: ["test-current.txt"],
+}
+
+filegroup {
+    name: "non-updatable-test-removed.txt",
+    srcs: ["test-removed.txt"],
+}
+
+filegroup {
+    name: "non-updatable-test-lint-baseline.txt",
+    srcs: ["test-lint-baseline.txt"],
+}
+
+java_api_contribution {
+    name: "api-stubs-docs-non-updatable-public-stubs",
+    api_surface: "public",
+    api_file: "current.txt",
+    visibility: [
+        "//build/orchestrator/apis",
+    ],
+}
+
+java_api_contribution {
+    name: "frameworks-base-core-api-module-lib-stubs",
+    api_surface: "module-lib",
+    api_file: "module-lib-current.txt",
+    visibility: [
+        "//build/orchestrator/apis",
+    ],
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index ac63513..211f603 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -15484,14 +15484,14 @@
     method @NonNull public float getMinDisplayRatioForHdrTransition();
     method @NonNull public float[] getRatioMax();
     method @NonNull public float[] getRatioMin();
-    method @NonNull public void setDisplayRatioForFullHdr(float);
-    method @NonNull public void setEpsilonHdr(float, float, float);
-    method @NonNull public void setEpsilonSdr(float, float, float);
+    method public void setDisplayRatioForFullHdr(@FloatRange(from=1.0f) float);
+    method public void setEpsilonHdr(float, float, float);
+    method public void setEpsilonSdr(float, float, float);
     method public void setGainmapContents(@NonNull android.graphics.Bitmap);
-    method @NonNull public void setGamma(float, float, float);
-    method @NonNull public void setMinDisplayRatioForHdrTransition(@FloatRange(from=1.0f) float);
-    method @NonNull public void setRatioMax(float, float, float);
-    method @NonNull public void setRatioMin(float, float, float);
+    method public void setGamma(float, float, float);
+    method public void setMinDisplayRatioForHdrTransition(@FloatRange(from=1.0f) float);
+    method public void setRatioMax(float, float, float);
+    method public void setRatioMin(float, float, float);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.graphics.Gainmap> CREATOR;
   }
@@ -27308,6 +27308,7 @@
     field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
     field public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; // 0x0
     field public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; // 0x1
+    field public static final String TV_MESSAGE_KEY_STREAM_ID = "android.media.tv.TvInputManager.stream_id";
     field public static final String TV_MESSAGE_TYPE_CLOSED_CAPTION = "CC";
     field public static final String TV_MESSAGE_TYPE_WATERMARK = "Watermark";
     field public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4; // 0x4
@@ -27436,6 +27437,7 @@
     method public boolean onTrackballEvent(android.view.MotionEvent);
     method public abstract boolean onTune(android.net.Uri);
     method public boolean onTune(android.net.Uri, android.os.Bundle);
+    method public void onTvMessage(@NonNull String, @NonNull android.os.Bundle);
     method public void onUnblockContent(android.media.tv.TvContentRating);
     method public void setOverlayViewEnabled(boolean);
   }
@@ -36889,6 +36891,7 @@
     method public String[] getDocumentStreamTypes(String, String);
     method public String getDocumentType(String) throws java.io.FileNotFoundException;
     method public final String getType(android.net.Uri);
+    method @Nullable public final String getTypeAnonymous(@NonNull android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
     method public boolean isChildDocument(String, String);
     method public String moveDocument(String, String, String) throws java.io.FileNotFoundException;
@@ -40640,9 +40643,11 @@
   }
 
   public class CredentialEntry implements android.os.Parcelable {
+    ctor public CredentialEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice);
     ctor public CredentialEntry(@NonNull android.service.credentials.BeginGetCredentialOption, @NonNull android.app.slice.Slice);
+    ctor public CredentialEntry(@NonNull String, @NonNull android.app.slice.Slice);
     method public int describeContents();
-    method @NonNull public android.service.credentials.BeginGetCredentialOption getBeginGetCredentialOption();
+    method @NonNull public String getBeginGetCredentialOptionId();
     method @NonNull public android.app.slice.Slice getSlice();
     method @NonNull public String getType();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a8a48b7..11a0f0b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2363,6 +2363,8 @@
     method @NonNull public String getTargetId();
     method @NonNull public java.util.List<java.lang.String> getTargetIds();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int ACTION_DELETE = 9; // 0x9
+    field public static final int ACTION_DISMISS = 10; // 0xa
     field public static final int ACTION_DRAGNDROP = 7; // 0x7
     field public static final int ACTION_LAUNCH_KEYBOARD_FOCUS = 6; // 0x6
     field public static final int ACTION_LAUNCH_TOUCH = 5; // 0x5
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a9ae225..a3bc392 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -899,6 +899,7 @@
     field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f;
     field public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // 0xc2368d6L
     field public static final long OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN = 208648326L; // 0xc6fb886L
+    field public static final long OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS = 237531167L; // 0xe28701fL
     field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
   }
 
@@ -1071,6 +1072,40 @@
 
 }
 
+package android.credentials {
+
+  public final class CredentialManager {
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.QUERY_ALL_PACKAGES, "android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS"}) public java.util.List<android.credentials.CredentialProviderInfo> getCredentialProviderServicesForTesting(int);
+    method public static boolean isServiceEnabled(@NonNull android.content.Context);
+    field public static final int PROVIDER_FILTER_ALL_PROVIDERS = 0; // 0x0
+    field public static final int PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY = 1; // 0x1
+    field public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2; // 0x2
+  }
+
+  public final class CredentialProviderInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<java.lang.String> getCapabilities();
+    method @NonNull public android.content.ComponentName getComponentName();
+    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 @NonNull public boolean hasCapability(@NonNull String);
+    method public boolean isEnabled();
+    method public boolean isSystemProvider();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialProviderInfo> CREATOR;
+  }
+
+  public static final class CredentialProviderInfo.Builder {
+    ctor public CredentialProviderInfo.Builder(@NonNull android.content.pm.ServiceInfo);
+    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 setSystemProvider(boolean);
+  }
+
+}
+
 package android.credentials.ui {
 
   public final class AuthenticationEntry implements android.os.Parcelable {
@@ -1921,6 +1956,10 @@
     method public void removeHardwareDevice(int);
   }
 
+  public class TvView extends android.view.ViewGroup {
+    method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
+  }
+
 }
 
 package android.media.tv.tuner {
@@ -3303,7 +3342,9 @@
   }
 
   @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+    method public void getBoundsOnScreen(@NonNull android.graphics.Rect, boolean);
     method public android.view.View getTooltipView();
+    method public void getWindowDisplayFrame(@NonNull android.graphics.Rect);
     method public boolean isAutofilled();
     method public static boolean isDefaultFocusHighlightEnabled();
     method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
@@ -3936,6 +3977,18 @@
     method public abstract void onTransactionReady(int, @NonNull android.view.SurfaceControl.Transaction);
   }
 
+  public class WindowInfosListenerForTest {
+    ctor public WindowInfosListenerForTest();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void addWindowInfosListener(@NonNull java.util.function.Consumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>>);
+    method public void removeWindowInfosListener(@NonNull java.util.function.Consumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>>);
+  }
+
+  public static class WindowInfosListenerForTest.WindowInfo {
+    field @NonNull public final android.graphics.Rect bounds;
+    field @NonNull public final String name;
+    field @NonNull public final android.os.IBinder windowToken;
+  }
+
   public class WindowOrganizer {
     ctor public WindowOrganizer();
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 738e2de..3969577 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -285,6 +285,28 @@
     ],
 }
 
+filegroup {
+    name: "hwbinder-stubs-srcs",
+    srcs: [
+        "android/os/HidlSupport.java",
+        "android/os/HidlMemory.java",
+        "android/os/HwBinder.java",
+        "android/os/HwBlob.java",
+        "android/os/HwParcel.java",
+        "android/os/IHwBinder.java",
+        "android/os/IHwInterface.java",
+        "android/os/DeadObjectException.java",
+        "android/os/DeadSystemException.java",
+        "android/os/NativeHandle.java",
+        "android/os/RemoteException.java",
+        "android/util/AndroidException.java",
+    ],
+    visibility: [
+        "//frameworks/base",
+        "//frameworks/base/api",
+    ],
+}
+
 cc_defaults {
     name: "incremental_default",
     cflags: [
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 87fe215..b4068db 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4208,8 +4208,8 @@
      * processes to reclaim memory; the system will take care of restarting
      * these processes in the future as needed.
      *
-     * <p class="note">On devices with a {@link Build.VERSION#SECURITY_PATCH} of 2022-12-01 or
-     * greater, third party applications can only use this API to kill their own processes.
+     * <p class="note">On devices that run Android 14 or higher,
+     * third party applications can only use this API to kill their own processes.
      * </p>
      *
      * @param packageName The name of the package whose processes are to
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 5cad1ae..ac92811 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -93,7 +93,7 @@
 
     private static final String TAG = "Instrumentation";
 
-    private static final long CONNECT_TIMEOUT_MILLIS = 5000;
+    private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
 
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 3957732..b6e83c8 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -19,6 +19,9 @@
 
 import com.android.server.LocalServices;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Stores a copy of the set of device policies maintained by {@link DevicePolicyManager} that
  * can be accessed from any place without risking dead locks.
@@ -61,6 +64,12 @@
     public abstract boolean canAdminGrantSensorsPermissions();
 
     /**
+     * Returns a list of package names for which all launcher shortcuts should be modified to be
+     * launched in the managed profile and badged accordingly.
+     */
+    public abstract List<String> getLauncherShortcutOverrides();
+
+    /**
      * Empty implementation.
      */
     private static class EmptyDevicePolicyCache extends DevicePolicyCache {
@@ -85,5 +94,9 @@
         public boolean canAdminGrantSensorsPermissions() {
             return false;
         }
+        @Override
+        public List<String> getLauncherShortcutOverrides() {
+            return new ArrayList<>();
+        }
     }
 }
diff --git a/core/java/android/app/search/SearchTargetEvent.java b/core/java/android/app/search/SearchTargetEvent.java
index d4915af..e8ef922 100644
--- a/core/java/android/app/search/SearchTargetEvent.java
+++ b/core/java/android/app/search/SearchTargetEvent.java
@@ -50,7 +50,9 @@
             ACTION_LAUNCH_TOUCH,
             ACTION_LAUNCH_KEYBOARD_FOCUS,
             ACTION_DRAGNDROP,
-            ACTION_SURFACE_INVISIBLE
+            ACTION_SURFACE_INVISIBLE,
+            ACTION_DELETE,
+            ACTION_DISMISS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionType {}
@@ -114,6 +116,16 @@
      */
     public static final int ACTION_SURFACE_INVISIBLE = 8;
 
+     /**
+     * Constant that defines user deleted a target.
+     */
+    public static final int ACTION_DELETE = 9;
+
+    /**
+     * Constant that defines user dismissed a target.
+     */
+    public static final int ACTION_DISMISS = 10;
+
     private SearchTargetEvent(@NonNull List<String> targetIds,
             @Nullable String location,
             @ActionType int actionType,
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index f8f2663..eb14cc4 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1156,6 +1156,34 @@
             264301586L; // buganizer id
 
     /**
+     * This change id forces the packages it is applied to sandbox {@link android.view.View} API to
+     * an activity bounds for:
+     *
+     * <p>{@link android.view.View#getLocationOnScreen},
+     * {@link android.view.View#getWindowVisibleDisplayFrame},
+     * {@link android.view.View}#getWindowDisplayFrame,
+     * {@link android.view.View}#getBoundsOnScreen.
+     *
+     * <p>For {@link android.view.View#getWindowVisibleDisplayFrame} and
+     * {@link android.view.View}#getWindowDisplayFrame this sandboxing is happening indirectly
+     * through
+     * {@link android.view.ViewRootImpl}#getWindowVisibleDisplayFrame,
+     * {@link android.view.ViewRootImpl}#getDisplayFrame respectively.
+     *
+     * <p>Some applications assume that they occupy the whole screen and therefore use the display
+     * coordinates in their calculations as if an activity is  positioned in the top-left corner of
+     * the screen, with left coordinate equal to 0. This may not be the case of applications in
+     * multi-window and in letterbox modes. This can lead to shifted or out of bounds UI elements in
+     * case the activity is Letterboxed or is in multi-window mode.
+     * @hide
+     */
+    @ChangeId
+    @Overridable
+    @Disabled
+    @TestApi
+    public static final long OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS = 237531167L; // buganizer id
+
+    /**
      * This change id is the gatekeeper for all treatments that force a given min aspect ratio.
      * Enabling this change will allow the following min aspect ratio treatments to be applied:
      * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 08cfbf7..96a42e2 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -33,6 +33,7 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.LauncherActivityInfoInternal;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -114,4 +115,5 @@
 
     String getShortcutIconUri(String callingPackage, String packageName, String shortcutId,
             int userId);
+    Map<String, LauncherActivityInfoInternal> getActivityOverrides(String callingPackage, int userId);
 }
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 16e720e..a4d5327 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -34,7 +34,6 @@
  */
 public class LauncherActivityInfo {
     private final PackageManager mPm;
-    private UserHandle mUser;
     private final LauncherActivityInfoInternal mInternal;
 
     /**
@@ -43,9 +42,8 @@
      * @param context The context for fetching resources.
 
      */
-    LauncherActivityInfo(Context context, UserHandle user, LauncherActivityInfoInternal internal) {
+    LauncherActivityInfo(Context context, LauncherActivityInfoInternal internal) {
         mPm = context.getPackageManager();
-        mUser = user;
         mInternal = internal;
     }
 
@@ -70,7 +68,7 @@
      * @return The UserHandle of the profile.
      */
     public UserHandle getUser() {
-        return mUser;
+        return mInternal.getUser();
     }
 
     /**
@@ -180,6 +178,6 @@
     public Drawable getBadgedIcon(int density) {
         Drawable originalIcon = getIcon(density);
 
-        return mPm.getUserBadgedIcon(originalIcon, mUser);
+        return mPm.getUserBadgedIcon(originalIcon, mInternal.getUser());
     }
 }
diff --git a/core/java/android/content/pm/LauncherActivityInfoInternal.java b/core/java/android/content/pm/LauncherActivityInfoInternal.java
index 46c415d..5aac97d 100644
--- a/core/java/android/content/pm/LauncherActivityInfoInternal.java
+++ b/core/java/android/content/pm/LauncherActivityInfoInternal.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 
 /**
  * @hide
@@ -30,23 +31,27 @@
     @NonNull private ActivityInfo mActivityInfo;
     @NonNull private ComponentName mComponentName;
     @NonNull private IncrementalStatesInfo mIncrementalStatesInfo;
+    @NonNull private UserHandle mUser;
 
     /**
      * @param info ActivityInfo from which to create the LauncherActivityInfo.
      * @param incrementalStatesInfo The package's states.
+     * @param user The user the activity info belongs to.
      */
     public LauncherActivityInfoInternal(@NonNull ActivityInfo info,
-            @NonNull IncrementalStatesInfo incrementalStatesInfo) {
+            @NonNull IncrementalStatesInfo incrementalStatesInfo,
+            @NonNull UserHandle user) {
         mActivityInfo = info;
         mComponentName = new ComponentName(info.packageName, info.name);
         mIncrementalStatesInfo = incrementalStatesInfo;
+        mUser = user;
     }
 
     public LauncherActivityInfoInternal(Parcel source) {
-        mActivityInfo = source.readParcelable(ActivityInfo.class.getClassLoader(), android.content.pm.ActivityInfo.class);
+        mActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
         mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name);
-        mIncrementalStatesInfo = source.readParcelable(
-                IncrementalStatesInfo.class.getClassLoader(), android.content.pm.IncrementalStatesInfo.class);
+        mIncrementalStatesInfo = source.readTypedObject(IncrementalStatesInfo.CREATOR);
+        mUser = source.readTypedObject(UserHandle.CREATOR);
     }
 
     public ComponentName getComponentName() {
@@ -57,6 +62,10 @@
         return mActivityInfo;
     }
 
+    public UserHandle getUser() {
+        return mUser;
+    }
+
     public IncrementalStatesInfo getIncrementalStatesInfo() {
         return mIncrementalStatesInfo;
     }
@@ -68,8 +77,9 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mActivityInfo, 0);
-        dest.writeParcelable(mIncrementalStatesInfo, 0);
+        dest.writeTypedObject(mActivityInfo, flags);
+        dest.writeTypedObject(mIncrementalStatesInfo, flags);
+        dest.writeTypedObject(mUser, flags);
     }
 
     public static final @android.annotation.NonNull Creator<LauncherActivityInfoInternal> CREATOR =
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index f8c4974..8989006 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -64,6 +64,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
@@ -793,13 +794,45 @@
             if (ai == null) {
                 return null;
             }
-            return new LauncherActivityInfo(mContext, user, ai);
+            return new LauncherActivityInfo(mContext, ai);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
     }
 
     /**
+     * Returns overrides for the activities that should be launched for the shortcuts of certain
+     * package names.
+     *
+     * @return {@link Map} whose keys are package names and whose values are the
+     * {@link LauncherActivityInfo}s that should be used for those packages' shortcuts. If there are
+     * no activity overrides, an empty {@link Map} will be returned.
+     *
+     * @hide
+     */
+    @NonNull
+    public Map<String, LauncherActivityInfo> getActivityOverrides() {
+        Map<String, LauncherActivityInfo> activityOverrides = new ArrayMap<>();
+        try {
+            Map<String, LauncherActivityInfoInternal> activityOverridesInternal =
+                    mService.getActivityOverrides(mContext.getPackageName(), mContext.getUserId());
+            for (Map.Entry<String, LauncherActivityInfoInternal> packageToOverride :
+                    activityOverridesInternal.entrySet()) {
+                activityOverrides.put(
+                        packageToOverride.getKey(),
+                        new LauncherActivityInfo(
+                                mContext,
+                                packageToOverride.getValue()
+                        )
+                );
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+        return activityOverrides;
+    }
+
+    /**
      * Starts a Main activity in the specified profile.
      *
      * @param component The ComponentName of the activity to launch
@@ -916,7 +949,7 @@
         }
         ArrayList<LauncherActivityInfo> lais = new ArrayList<>();
         for (LauncherActivityInfoInternal internal : internals.getList()) {
-            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, user, internal);
+            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, internal);
             if (DEBUG) {
                 Log.v(TAG, "Returning activity for profile " + user + " : "
                         + lai.getComponentName());
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index f0230e7..0806f1d 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -26,12 +26,12 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentSender;
-import android.content.pm.ServiceInfo;
 import android.os.CancellationSignal;
 import android.os.ICancellationSignal;
 import android.os.OutcomeReceiver;
@@ -75,21 +75,21 @@
      *
      * @hide
      */
-    public static final int PROVIDER_FILTER_ALL_PROVIDERS = 0;
+    @TestApi public static final int PROVIDER_FILTER_ALL_PROVIDERS = 0;
 
     /**
      * Returns system credential providers only.
      *
      * @hide
      */
-    public static final int PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY = 1;
+    @TestApi public static final int PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY = 1;
 
     /**
      * Returns user credential providers only.
      *
      * @hide
      */
-    public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2;
+    @TestApi public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2;
 
     private final Context mContext;
     private final ICredentialManager mService;
@@ -263,44 +263,6 @@
     }
 
     /**
-     * Gets a list of all user configurable credential providers registered on the system. This API
-     * is intended for browsers and settings apps.
-     *
-     * @param cancellationSignal an optional signal that allows for cancelling this call
-     * @param executor the callback will take place on this {@link Executor}
-     * @param callback the callback invoked when the request succeeds or fails
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
-    public void listEnabledProviders(
-            @Nullable CancellationSignal cancellationSignal,
-            @CallbackExecutor @NonNull Executor executor,
-            @NonNull
-                    OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
-                            callback) {
-        requireNonNull(executor, "executor must not be null");
-        requireNonNull(callback, "callback must not be null");
-
-        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
-            Log.w(TAG, "listEnabledProviders already canceled");
-            return;
-        }
-
-        ICancellationSignal cancelRemote = null;
-        try {
-            cancelRemote =
-                    mService.listEnabledProviders(
-                            new ListEnabledProvidersTransport(executor, callback));
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-
-        if (cancellationSignal != null && cancelRemote != null) {
-            cancellationSignal.setRemote(cancelRemote);
-        }
-    }
-
-    /**
      * Sets a list of all user configurable credential providers registered on the system. This API
      * is intended for settings apps.
      *
@@ -348,36 +310,43 @@
     }
 
     /**
-     * Returns the list of ServiceInfo for all discovered credential providers on this device.
+     * Returns the list of CredentialProviderInfo for all discovered credential providers on this
+     * device but will include test system providers as well.
      *
      * @hide
      */
     @NonNull
-    @RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
-    public List<ServiceInfo> getCredentialProviderServicesForTesting(
-            @ProviderFilter int providerFilter) {
+    @TestApi
+    @RequiresPermission(
+      anyOf = {
+        android.Manifest.permission.QUERY_ALL_PACKAGES,
+        android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS
+      })
+    public List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
+             @ProviderFilter int providerFilter) {
         try {
-            return mService.getCredentialProviderServices(
-                    mContext.getUserId(),
-                    /* disableSystemAppVerificationForTests= */ true,
-                    providerFilter);
+            return mService.getCredentialProviderServicesForTesting(providerFilter);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Returns the list of ServiceInfo for all discovered credential providers on this device.
+     * Returns the list of CredentialProviderInfo for all discovered credential providers on this
+     * device.
      *
      * @hide
      */
     @NonNull
-    @RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
-    public List<ServiceInfo> getCredentialProviderServices(
+    @RequiresPermission(
+      anyOf = {
+        android.Manifest.permission.QUERY_ALL_PACKAGES,
+        android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS
+      })
+    public List<CredentialProviderInfo> getCredentialProviderServices(
             int userId, @ProviderFilter int providerFilter) {
         try {
-            return mService.getCredentialProviderServices(
-                    userId, /* disableSystemAppVerificationForTests= */ false, providerFilter);
+            return mService.getCredentialProviderServices(userId, providerFilter);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -388,7 +357,9 @@
      *
      * @hide
      */
-    public static boolean isServiceEnabled(Context context) {
+    @TestApi
+    public static boolean isServiceEnabled(@NonNull Context context) {
+        requireNonNull(context, "context must not be null");
         if (context == null) {
             return false;
         }
@@ -578,33 +549,6 @@
         }
     }
 
-    private static class ListEnabledProvidersTransport extends IListEnabledProvidersCallback.Stub {
-        // TODO: listen for cancellation to release callback.
-
-        private final Executor mExecutor;
-        private final OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
-                mCallback;
-
-        private ListEnabledProvidersTransport(
-                Executor executor,
-                OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
-                        callback) {
-            mExecutor = executor;
-            mCallback = callback;
-        }
-
-        @Override
-        public void onResponse(ListEnabledProvidersResponse response) {
-            mExecutor.execute(() -> mCallback.onResult(response));
-        }
-
-        @Override
-        public void onError(String errorType, String message) {
-            mExecutor.execute(
-                    () -> mCallback.onError(new ListEnabledProvidersException(errorType, message)));
-        }
-    }
-
     private static class SetEnabledProvidersTransport extends ISetEnabledProvidersCallback.Stub {
         // TODO: listen for cancellation to release callback.
 
diff --git a/core/java/android/credentials/IListEnabledProvidersCallback.aidl b/core/java/android/credentials/CredentialProviderInfo.aidl
similarity index 63%
rename from core/java/android/credentials/IListEnabledProvidersCallback.aidl
rename to core/java/android/credentials/CredentialProviderInfo.aidl
index 3a8e25ed..30b7742 100644
--- a/core/java/android/credentials/IListEnabledProvidersCallback.aidl
+++ b/core/java/android/credentials/CredentialProviderInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 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.
@@ -16,14 +16,4 @@
 
 package android.credentials;
 
-import android.credentials.ListEnabledProvidersResponse;
-
-/**
- * Listener for an listEnabledProviders request.
- *
- * @hide
- */
-interface IListEnabledProvidersCallback {
-    oneway void onResponse(in ListEnabledProvidersResponse response);
-    oneway void onError(String errorType, String message);
-}
\ No newline at end of file
+parcelable CredentialProviderInfo;
\ No newline at end of file
diff --git a/core/java/android/credentials/CredentialProviderInfo.java b/core/java/android/credentials/CredentialProviderInfo.java
new file mode 100644
index 0000000..7276770
--- /dev/null
+++ b/core/java/android/credentials/CredentialProviderInfo.java
@@ -0,0 +1,215 @@
+/*
+ * 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 android.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@link ServiceInfo} and meta-data about a credential provider.
+ *
+ * @hide
+ */
+@TestApi
+public final class CredentialProviderInfo implements Parcelable {
+    @NonNull private final ServiceInfo mServiceInfo;
+    @NonNull private final List<String> mCapabilities = new ArrayList<>();
+    @Nullable private final CharSequence mOverrideLabel;
+    private final boolean mIsSystemProvider;
+    private final boolean mIsEnabled;
+
+    /**
+     * Constructs an information instance of the credential provider.
+     *
+     * @param builder the builder object.
+     */
+    private CredentialProviderInfo(@NonNull Builder builder) {
+        mServiceInfo = builder.mServiceInfo;
+        mCapabilities.addAll(builder.mCapabilities);
+        mIsSystemProvider = builder.mIsSystemProvider;
+        mIsEnabled = builder.mIsEnabled;
+        mOverrideLabel = builder.mOverrideLabel;
+    }
+
+    /** Returns true if the service supports the given {@code credentialType}, false otherwise. */
+    @NonNull
+    public boolean hasCapability(@NonNull String credentialType) {
+        return mCapabilities.contains(credentialType);
+    }
+
+    /** Returns the service info. */
+    @NonNull
+    public ServiceInfo getServiceInfo() {
+        return mServiceInfo;
+    }
+
+    /** Returns whether it is a system provider. */
+    public boolean isSystemProvider() {
+        return mIsSystemProvider;
+    }
+
+    /** Returns the service icon. */
+    @Nullable
+    public Drawable getServiceIcon(@NonNull Context context) {
+        return mServiceInfo.loadIcon(context.getPackageManager());
+    }
+
+    /** Returns the service label. */
+    @Nullable
+    public CharSequence getLabel(@NonNull Context context) {
+        if (mOverrideLabel != null) {
+            return mOverrideLabel;
+        }
+        return mServiceInfo.loadSafeLabel(context.getPackageManager());
+    }
+
+    /** Returns a list of capabilities this provider service can support. */
+    @NonNull
+    public List<String> getCapabilities() {
+        return Collections.unmodifiableList(mCapabilities);
+    }
+
+    /** Returns whether the provider is enabled by the user. */
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /** Returns the component name for the service. */
+    @NonNull
+    public ComponentName getComponentName() {
+        return mServiceInfo.getComponentName();
+    }
+
+    @Override
+    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);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "CredentialProviderInfo {"
+                + "serviceInfo="
+                + mServiceInfo
+                + ", "
+                + "isSystemProvider="
+                + mIsSystemProvider
+                + ", "
+                + "isEnabled="
+                + mIsEnabled
+                + ", "
+                + "overrideLabel="
+                + mOverrideLabel
+                + ", "
+                + "capabilities="
+                + String.join(",", mCapabilities)
+                + "}";
+    }
+
+    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);
+    }
+
+    public static final @NonNull Parcelable.Creator<CredentialProviderInfo> CREATOR =
+            new Parcelable.Creator<CredentialProviderInfo>() {
+                @Override
+                public CredentialProviderInfo[] newArray(int size) {
+                    return new CredentialProviderInfo[size];
+                }
+
+                @Override
+                public CredentialProviderInfo createFromParcel(@NonNull Parcel in) {
+                    return new CredentialProviderInfo(in);
+                }
+            };
+
+    /** A builder for {@link CredentialProviderInfo} objects. */
+    public static final class Builder {
+
+        @NonNull private ServiceInfo mServiceInfo;
+        @NonNull private List<String> mCapabilities = new ArrayList<>();
+        private boolean mIsSystemProvider = false;
+        private boolean mIsEnabled = false;
+        @Nullable private CharSequence mOverrideLabel = null;
+
+        /**
+         * Creates a new builder.
+         *
+         * @param serviceInfo the service info of the credential provider service.
+         */
+        public Builder(@NonNull ServiceInfo serviceInfo) {
+            mServiceInfo = serviceInfo;
+        }
+
+        /** Sets whether it is a system provider. */
+        public @NonNull Builder setSystemProvider(boolean isSystemProvider) {
+            mIsSystemProvider = isSystemProvider;
+            return this;
+        }
+
+        /**
+         * Sets the label to be used instead of getting from the system (for unit tests).
+         *
+         * @hide
+         */
+        public @NonNull Builder setOverrideLabel(@NonNull CharSequence overrideLabel) {
+            mOverrideLabel = overrideLabel;
+            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 whether it is enabled by the user. */
+        public @NonNull Builder setEnabled(boolean isEnabled) {
+            mIsEnabled = isEnabled;
+            return this;
+        }
+
+        /** Builds a new {@link CredentialProviderInfo} instance. */
+        public @NonNull CredentialProviderInfo build() {
+            return new CredentialProviderInfo(this);
+        }
+    }
+}
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index 625fc8a..8c2cb5a 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -18,7 +18,7 @@
 
 import java.util.List;
 
-import android.content.pm.ServiceInfo;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.ClearCredentialStateRequest;
 import android.credentials.CreateCredentialRequest;
 import android.credentials.GetCredentialRequest;
@@ -27,7 +27,6 @@
 import android.credentials.IClearCredentialStateCallback;
 import android.credentials.ICreateCredentialCallback;
 import android.credentials.IGetCredentialCallback;
-import android.credentials.IListEnabledProvidersCallback;
 import android.credentials.ISetEnabledProvidersCallback;
 import android.content.ComponentName;
 import android.os.ICancellationSignal;
@@ -45,8 +44,6 @@
 
     @nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
 
-    @nullable ICancellationSignal listEnabledProviders(in IListEnabledProvidersCallback callback);
-
     void setEnabledProviders(in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
 
     void registerCredentialDescription(in RegisterCredentialDescriptionRequest request, String callingPackage);
@@ -55,6 +52,8 @@
 
     boolean isEnabledCredentialProviderService(in ComponentName componentName, String callingPackage);
 
-    List<ServiceInfo> getCredentialProviderServices(in int userId, in boolean disableSystemAppVerificationForTests, in int providerFilter);
+    List<CredentialProviderInfo> getCredentialProviderServices(in int userId, in int providerFilter);
+
+    List<CredentialProviderInfo> getCredentialProviderServicesForTesting(in int providerFilter);
 }
 
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 067ae4d..490e55b 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -28,6 +28,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
+import android.view.ContentRecordingSession;
 import android.view.Display;
 import android.view.Surface;
 
@@ -53,6 +54,8 @@
     private final int mDisplayIdToMirror;
     private final boolean mWindowManagerMirroringEnabled;
     private ArraySet<String> mDisplayCategories = null;
+    @Nullable
+    private ContentRecordingSession mContentRecordingSession;
     private final float mRequestedRefreshRate;
 
     private VirtualDisplayConfig(
@@ -65,6 +68,7 @@
             @Nullable String uniqueId,
             int displayIdToMirror,
             boolean windowManagerMirroringEnabled,
+            ContentRecordingSession session,
             @NonNull ArraySet<String> displayCategories,
             float requestedRefreshRate) {
         mName = name;
@@ -76,6 +80,7 @@
         mUniqueId = uniqueId;
         mDisplayIdToMirror = displayIdToMirror;
         mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
+        mContentRecordingSession = session;
         mDisplayCategories = displayCategories;
         mRequestedRefreshRate = requestedRefreshRate;
     }
@@ -156,6 +161,17 @@
     }
 
     /**
+     * Returns the recording session associated with this VirtualDisplay. Only used for
+     * recording via {@link MediaProjection}.
+     *
+     * @hide
+     */
+    @Nullable
+    public ContentRecordingSession getContentRecordingSession() {
+        return mContentRecordingSession;
+    }
+
+    /**
      * Returns the display categories.
      *
      * @see Builder#setDisplayCategories
@@ -186,6 +202,7 @@
         dest.writeString8(mUniqueId);
         dest.writeInt(mDisplayIdToMirror);
         dest.writeBoolean(mWindowManagerMirroringEnabled);
+        dest.writeTypedObject(mContentRecordingSession, flags);
         dest.writeArraySet(mDisplayCategories);
         dest.writeFloat(mRequestedRefreshRate);
     }
@@ -211,6 +228,7 @@
                 && Objects.equals(mUniqueId, that.mUniqueId)
                 && mDisplayIdToMirror == that.mDisplayIdToMirror
                 && mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
+                && Objects.equals(mContentRecordingSession, that.mContentRecordingSession)
                 && Objects.equals(mDisplayCategories, that.mDisplayCategories)
                 && mRequestedRefreshRate == that.mRequestedRefreshRate;
     }
@@ -219,8 +237,8 @@
     public int hashCode() {
         int hashCode = Objects.hash(
                 mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
-                mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
-                mRequestedRefreshRate);
+                mDisplayIdToMirror, mWindowManagerMirroringEnabled, mContentRecordingSession,
+                mDisplayCategories, mRequestedRefreshRate);
         return hashCode;
     }
 
@@ -237,6 +255,7 @@
                 + " mUniqueId=" + mUniqueId
                 + " mDisplayIdToMirror=" + mDisplayIdToMirror
                 + " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
+                + " mContentRecordingSession=" + mContentRecordingSession
                 + " mDisplayCategories=" + mDisplayCategories
                 + " mRequestedRefreshRate=" + mRequestedRefreshRate
                 + ")";
@@ -252,6 +271,7 @@
         mUniqueId = in.readString8();
         mDisplayIdToMirror = in.readInt();
         mWindowManagerMirroringEnabled = in.readBoolean();
+        mContentRecordingSession = in.readTypedObject(ContentRecordingSession.CREATOR);
         mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
         mRequestedRefreshRate = in.readFloat();
     }
@@ -283,6 +303,8 @@
         private String mUniqueId = null;
         private int mDisplayIdToMirror = DEFAULT_DISPLAY;
         private boolean mWindowManagerMirroringEnabled = false;
+        @Nullable
+        private ContentRecordingSession mContentRecordingSession;
         private ArraySet<String> mDisplayCategories = new ArraySet<>();
         private float mRequestedRefreshRate = 0.0f;
 
@@ -375,6 +397,18 @@
         }
 
         /**
+         * Sets the recording session associated with this {@link VirtualDisplay}. Only used for
+         * recording via {@link MediaProjection}.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setContentRecordingSession(@Nullable ContentRecordingSession session) {
+            mContentRecordingSession = session;
+            return this;
+        }
+
+        /**
          * Sets the display categories.
          *
          * <p>The categories of the display indicate the type of activities allowed to run on that
@@ -435,6 +469,7 @@
                     mUniqueId,
                     mDisplayIdToMirror,
                     mWindowManagerMirroringEnabled,
+                    mContentRecordingSession,
                     mDisplayCategories,
                     mRequestedRefreshRate);
         }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index b749d69..cacde7f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -26,6 +26,8 @@
 import android.annotation.SystemApi;
 import android.annotation.UserIdInt;
 import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.OnActivityPausedListener;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -1470,11 +1472,17 @@
         if (activity == null || intent == null) {
             throw new NullPointerException();
         }
+        if (!activity.isResumed()) {
+            throw new IllegalStateException("Foreground dispatch can only be enabled " +
+                    "when your activity is resumed");
+        }
         try {
             TechListParcel parcel = null;
             if (techLists != null && techLists.length > 0) {
                 parcel = new TechListParcel(techLists);
             }
+            ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
+                    mForegroundDispatchListener);
             sService.setForegroundDispatch(intent, filters, parcel);
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
@@ -1502,8 +1510,25 @@
                 throw new UnsupportedOperationException();
             }
         }
+        ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity,
+                mForegroundDispatchListener);
+        disableForegroundDispatchInternal(activity, false);
+    }
+
+    OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() {
+        @Override
+        public void onPaused(Activity activity) {
+            disableForegroundDispatchInternal(activity, true);
+        }
+    };
+
+    void disableForegroundDispatchInternal(Activity activity, boolean force) {
         try {
             sService.setForegroundDispatch(null, null, null);
+            if (!force && !activity.isResumed()) {
+                throw new IllegalStateException("You must disable foreground dispatching " +
+                        "while your activity is still resumed");
+            }
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
         }
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 07d5001..5b527c7 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -979,6 +979,19 @@
     }
 
     /**
+     * An unrestricted version of getType, which does not reveal sensitive information
+     */
+    @Override
+    public final @Nullable String getTypeAnonymous(@NonNull Uri uri) {
+        switch (mMatcher.match(uri)) {
+            case MATCH_ROOT:
+                return DocumentsContract.Root.MIME_TYPE_ITEM;
+            default:
+                return null;
+        }
+    }
+
+    /**
      * Implementation is provided by the parent class. Can be overridden to
      * provide additional functionality, but subclasses <em>must</em> always
      * call the superclass. If the superclass returns {@code null}, the subclass
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ef00774..045ba1f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5678,6 +5678,36 @@
         public static final String LOCALE_PREFERENCES = "locale_preferences";
 
         /**
+         * Setting to enable camera flash notification feature.
+         * <ul>
+         *     <li> 0 = Off
+         *     <li> 1 = On
+         * </ul>
+         * @hide
+         */
+        public static final String CAMERA_FLASH_NOTIFICATION = "camera_flash_notification";
+
+        /**
+         * Setting to enable screen flash notification feature.
+         * <ul>
+         *     <li> 0 = Off
+         *     <li> 1 = On
+         * </ul>
+         *  @hide
+         */
+        public static final String SCREEN_FLASH_NOTIFICATION = "screen_flash_notification";
+
+        /**
+         * Integer property that specifes the color for screen flash notification as a
+         * packed 32-bit color.
+         *
+         * @see android.graphics.Color#argb
+         * @hide
+         */
+        public static final String SCREEN_FLASH_NOTIFICATION_COLOR =
+                "screen_flash_notification_color_global";
+
+        /**
          * IMPORTANT: If you add a new public settings you also have to add it to
          * PUBLIC_SETTINGS below. If the new setting is hidden you have to add
          * it to PRIVATE_SETTINGS below. Also add a validator that can validate
@@ -5810,6 +5840,9 @@
             PRIVATE_SETTINGS.add(TOUCHPAD_NATURAL_SCROLLING);
             PRIVATE_SETTINGS.add(TOUCHPAD_TAP_TO_CLICK);
             PRIVATE_SETTINGS.add(TOUCHPAD_RIGHT_CLICK_ZONE);
+            PRIVATE_SETTINGS.add(CAMERA_FLASH_NOTIFICATION);
+            PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION);
+            PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION_COLOR);
         }
 
         /**
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
index 7e98bc7..e9cebd2 100644
--- a/core/java/android/service/credentials/CredentialEntry.java
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -27,6 +27,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * A credential entry that is to be displayed on the account selector that is presented to the
  * user.
@@ -56,7 +58,7 @@
 @SuppressLint("ParcelNotFinal")
 public class CredentialEntry implements Parcelable {
     /** The request option that corresponds to this entry. **/
-    private final @Nullable BeginGetCredentialOption mBeginGetCredentialOption;
+    private final @Nullable String mBeginGetCredentialOptionId;
 
     /** The type of the credential entry to be shown on the UI. */
     private final @NonNull String mType;
@@ -72,19 +74,51 @@
      * to respond to query phase {@link CredentialProviderService#onBeginGetCredential}
      * credential retrieval requests.
      *
+     * @param beginGetCredentialOptionId the beginGetCredentialOptionId to be retrieved from
+     * {@link BeginGetCredentialOption#getId()} - the request option for which this CredentialEntry
+     *                                   is being constructed This helps maintain an association
+     *                                   such that when the user selects this entry, providers can
+     *                                   receive the complete corresponding
+     *                                   {@link GetCredentialRequest}.
+     * @param type the type of the credential for which this credential entry is being created
+     * @param slice the slice containing the metadata to be shown on the UI. Must be
+     *              constructed through the androidx.credentials jetpack library.
+     *
+     * @throws IllegalArgumentException If {@code beginGetCredentialOptionId} or {@code type}
+     * is null, or empty
+     */
+    public CredentialEntry(@NonNull String beginGetCredentialOptionId, @NonNull String type,
+            @NonNull Slice slice) {
+        mBeginGetCredentialOptionId = Preconditions.checkStringNotEmpty(
+                beginGetCredentialOptionId, "beginGetCredentialOptionId must not be "
+                        + "null, or empty");
+        mType = Preconditions.checkStringNotEmpty(type, "type must not be null, or "
+                + "empty");
+        mSlice = requireNonNull(slice, "slice must not be null");
+    }
+
+    /**
+     * Creates an entry that is associated with a {@link BeginGetCredentialOption} request.
+     * Providers must use this constructor when they extend from {@link CredentialProviderService}
+     * to respond to query phase {@link CredentialProviderService#onBeginGetCredential}
+     * credential retrieval requests.
+     *
      * @param beginGetCredentialOption the request option for which this credential entry is
      *                                 being constructed This helps maintain an association,
      *                                 such that when the user selects this entry, providers
-     *                                 can receive the conmplete corresponding request.
+     *                                 can receive the complete corresponding request.
      * @param slice the slice containing the metadata to be shown on the UI. Must be
      *              constructed through the androidx.credentials jetpack library.
      */
     public CredentialEntry(@NonNull BeginGetCredentialOption beginGetCredentialOption,
             @NonNull Slice slice) {
-        mBeginGetCredentialOption = requireNonNull(beginGetCredentialOption,
-                "beginGetCredentialOption must not be null");
-        mType = requireNonNull(mBeginGetCredentialOption.getType(),
-                "type must not be null");
+        requireNonNull(beginGetCredentialOption, "beginGetCredentialOption must not"
+                + " be null");
+        mBeginGetCredentialOptionId = Preconditions.checkStringNotEmpty(
+                beginGetCredentialOption.getId(), "Id in beginGetCredentialOption "
+                        + "must not be null");
+        mType = Preconditions.checkStringNotEmpty(beginGetCredentialOption.getType(),
+                "type in beginGetCredentialOption must not be null");
         mSlice = requireNonNull(slice, "slice must not be null");
     }
 
@@ -97,11 +131,9 @@
      * @param slice the slice containing the metadata to be shown on the UI. Must be
      *              constructed through the androidx.credentials jetpack library.
      *
-     * @hide
      */
-    // TODO: Unhide this constructor when the registry APIs are stable
     public CredentialEntry(@NonNull String type, @NonNull Slice slice) {
-        mBeginGetCredentialOption = null;
+        mBeginGetCredentialOptionId = null;
         mType = requireNonNull(type, "type must not be null");
         mSlice = requireNonNull(slice, "slice must not be null");
     }
@@ -110,7 +142,7 @@
         requireNonNull(in, "parcel must not be null");
         mType = in.readString8();
         mSlice = in.readTypedObject(Slice.CREATOR);
-        mBeginGetCredentialOption = in.readTypedObject(BeginGetCredentialOption.CREATOR);
+        mBeginGetCredentialOptionId = in.readString8();
     }
 
     @NonNull
@@ -136,15 +168,16 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString8(mType);
         dest.writeTypedObject(mSlice, flags);
-        dest.writeTypedObject(mBeginGetCredentialOption, flags);
+        dest.writeString8(mBeginGetCredentialOptionId);
     }
 
     /**
-     * Returns the request option for which this credential entry has been constructed.
+     * Returns the id of the {@link BeginGetCredentialOption} for which this credential
+     * entry has been constructed.
      */
     @NonNull
-    public BeginGetCredentialOption getBeginGetCredentialOption() {
-        return mBeginGetCredentialOption;
+    public String getBeginGetCredentialOptionId() {
+        return mBeginGetCredentialOptionId;
     }
 
     /**
diff --git a/core/java/android/service/credentials/CredentialProviderInfo.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
similarity index 69%
rename from core/java/android/service/credentials/CredentialProviderInfo.java
rename to core/java/android/service/credentials/CredentialProviderInfoFactory.java
index b5464db..fd9360f 100644
--- a/core/java/android/service/credentials/CredentialProviderInfo.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -34,40 +34,27 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.credentials.CredentialManager;
-import android.graphics.drawable.Drawable;
+import android.credentials.CredentialProviderInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.text.TextUtils;
+import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
- * {@link ServiceInfo} and meta-data about a credential provider.
+ * {@link CredentialProviderInfo} generator.
  *
  * @hide
  */
-public final class CredentialProviderInfo {
-    private static final String TAG = "CredentialProviderInfo";
-
-    @NonNull
-    private final ServiceInfo mServiceInfo;
-    @NonNull
-    private final List<String> mCapabilities;
-
-    @NonNull
-    private final Context mContext;
-    @Nullable
-    private final Drawable mIcon;
-    @Nullable
-    private final CharSequence mLabel;
-    private final boolean mIsSystemProvider;
+public final class CredentialProviderInfoFactory {
+    private static final String TAG = "CredentialProviderInfoFactory";
 
     /**
      * Constructs an information instance of the credential provider.
@@ -79,14 +66,18 @@
      * @throws PackageManager.NameNotFoundException If provider service is not found
      * @throws SecurityException If provider does not require the relevant permission
      */
-    public CredentialProviderInfo(@NonNull Context context,
-            @NonNull ComponentName serviceComponent, int userId, boolean isSystemProvider)
+    public static CredentialProviderInfo create(
+            @NonNull Context context,
+            @NonNull ComponentName serviceComponent,
+            int userId,
+            boolean isSystemProvider)
             throws PackageManager.NameNotFoundException {
-        this(
+        return create(
                 context,
                 getServiceInfoOrThrow(serviceComponent, userId),
                 isSystemProvider,
-                /* disableSystemAppVerificationForTests= */ false);
+                /* disableSystemAppVerificationForTests= */ false,
+                /* isEnabled= */ false);
     }
 
     /**
@@ -98,13 +89,16 @@
      * @param isSystemProvider whether the provider app is a system provider
      * @param disableSystemAppVerificationForTests whether to disable system app permission
      *     verification so that tests can install system providers
+     * @param isEnabled whether the user enabled this provider
      * @throws SecurityException If provider does not require the relevant permission
      */
-    public CredentialProviderInfo(
+    public static CredentialProviderInfo create(
             @NonNull Context context,
             @NonNull ServiceInfo serviceInfo,
             boolean isSystemProvider,
-            boolean disableSystemAppVerificationForTests) {
+            boolean disableSystemAppVerificationForTests,
+            boolean isEnabled)
+            throws SecurityException {
         verifyProviderPermission(serviceInfo);
         if (isSystemProvider) {
             if (!isValidSystemProvider(
@@ -114,23 +108,11 @@
                         "Provider is not a valid system provider: " + serviceInfo);
             }
         }
-        mIsSystemProvider = isSystemProvider;
-        mContext =  requireNonNull(context, "context must not be null");
-        mServiceInfo = requireNonNull(serviceInfo, "serviceInfo must not be null");
-        mCapabilities = new ArrayList<>();
-        mIcon = mServiceInfo.loadIcon(mContext.getPackageManager());
-        mLabel =
-                mServiceInfo.loadSafeLabel(
-                        mContext.getPackageManager(),
-                        0 /* do not ellipsize */,
-                        TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
-        Log.i(
-                TAG,
-                "mLabel is : "
-                        + mLabel
-                        + ", for: "
-                        + mServiceInfo.getComponentName().flattenToString());
-        populateProviderCapabilities(context, serviceInfo);
+
+        return populateMetadata(context, serviceInfo)
+                .setSystemProvider(isSystemProvider)
+                .setEnabled(isEnabled)
+                .build();
     }
 
     private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException {
@@ -138,19 +120,14 @@
         if (permission.equals(serviceInfo.permission)) {
             return;
         }
-
-        Slog.e(
-                TAG,
-                "Credential Provider Service from : "
-                        + serviceInfo.packageName
-                        + "does not require permission"
-                        + permission);
         throw new SecurityException(
                 "Service does not require the expected permission : " + permission);
     }
 
     private static boolean isSystemProviderWithValidPermission(
             ServiceInfo serviceInfo, Context context) {
+        requireNonNull(context, "context must not be null");
+
         final String permission = Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE;
         try {
             ApplicationInfo appInfo =
@@ -177,67 +154,88 @@
             Context context,
             ServiceInfo serviceInfo,
             boolean disableSystemAppVerificationForTests) {
-        boolean isValidSystemTestProvider =
-                isTestSystemProvider(serviceInfo, disableSystemAppVerificationForTests);
-        if (isValidSystemTestProvider) {
-            return true;
+        requireNonNull(context, "context must not be null");
+
+        if (disableSystemAppVerificationForTests) {
+            Bundle metadata = serviceInfo.metaData;
+            if (metadata == null) {
+                Slog.e(TAG, "isValidSystemProvider - metadata is null: " + serviceInfo);
+                return false;
+            }
+            return metadata.getBoolean(
+                    CredentialProviderService.TEST_SYSTEM_PROVIDER_META_DATA_KEY);
         }
+
         return isSystemProviderWithValidPermission(serviceInfo, context);
     }
 
-    private static boolean isTestSystemProvider(
-            ServiceInfo serviceInfo, boolean disableSystemAppVerificationForTests) {
-        if (!disableSystemAppVerificationForTests) {
-            return false;
-        }
+    private static CredentialProviderInfo.Builder populateMetadata(
+            @NonNull Context context, ServiceInfo serviceInfo) {
+        requireNonNull(context, "context must not be null");
 
-        Bundle metadata = serviceInfo.metaData;
-        if (metadata == null) {
-            Slog.e(TAG, "metadata is null: " + serviceInfo);
-            return false;
-        }
-        return metadata.getBoolean(CredentialProviderService.TEST_SYSTEM_PROVIDER_META_DATA_KEY);
-    }
-
-    private void populateProviderCapabilities(@NonNull Context context, ServiceInfo serviceInfo) {
+        final CredentialProviderInfo.Builder builder =
+                new CredentialProviderInfo.Builder(serviceInfo);
         final PackageManager pm = context.getPackageManager();
+
+        // 1. Get the metadata for the service.
+        final Bundle metadata = serviceInfo.metaData;
+        if (metadata == null) {
+            Log.i(TAG, "populateMetadata - metadata is null");
+            return builder;
+        }
+
+        // 2. Extract the capabilities from the bundle.
         try {
-            Bundle metadata = serviceInfo.metaData;
             Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
             if (metadata == null || resources == null) {
-                Log.i(TAG, "populateProviderCapabilities - metadata or resources is null");
-                return;
+                Log.i(TAG, "populateMetadata - resources is null");
+                return builder;
             }
 
-            String[] capabilities = resources.getStringArray(metadata.getInt(
-                    CredentialProviderService.CAPABILITY_META_DATA_KEY));
-            if (capabilities == null || capabilities.length == 0) {
-                Slog.i(TAG, "No capabilities found for provider:" + serviceInfo.packageName);
-                return;
-            }
-
-            for (String capability : capabilities) {
-                if (capability.isEmpty()) {
-                    Slog.i(TAG, "Skipping empty capability");
-                    continue;
-                }
-                Slog.i(TAG, "Capabilities found for provider: " + capability);
-                mCapabilities.add(capability);
-            }
+            builder.addCapabilities(populateProviderCapabilities(resources, metadata, serviceInfo));
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(TAG, e.getMessage());
-        } catch (Resources.NotFoundException e) {
-            Slog.e(TAG, e.getMessage());
         }
+
+        return builder;
     }
 
-    private static ServiceInfo getServiceInfoOrThrow(@NonNull ComponentName serviceComponent,
-            int userId) throws PackageManager.NameNotFoundException {
+    private static List<String> populateProviderCapabilities(
+            Resources resources, Bundle metadata, ServiceInfo serviceInfo) {
+        List<String> output = new ArrayList<>();
+        String[] capabilities = new String[0];
+
         try {
-            ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(
-                    serviceComponent,
-                    PackageManager.GET_META_DATA,
-                    userId);
+            capabilities =
+                    resources.getStringArray(
+                            metadata.getInt(CredentialProviderService.CAPABILITY_META_DATA_KEY));
+        } catch (Resources.NotFoundException e) {
+            Slog.e(TAG, "Failed to get capabilities: " + e.getMessage());
+        }
+
+        if (capabilities == null || capabilities.length == 0) {
+            Slog.e(TAG, "No capabilities found for provider:" + serviceInfo.packageName);
+            return output;
+        }
+
+        for (String capability : capabilities) {
+            if (capability.isEmpty()) {
+                Slog.e(TAG, "Skipping empty capability");
+                continue;
+            }
+            Slog.e(TAG, "Capabilities found for provider: " + capability);
+            output.add(capability);
+        }
+        return output;
+    }
+
+    private static ServiceInfo getServiceInfoOrThrow(
+            @NonNull ComponentName serviceComponent, int userId)
+            throws PackageManager.NameNotFoundException {
+        try {
+            ServiceInfo si =
+                    AppGlobals.getPackageManager()
+                            .getServiceInfo(serviceComponent, PackageManager.GET_META_DATA, userId);
             if (si != null) {
                 return si;
             }
@@ -256,6 +254,8 @@
             @NonNull Context context,
             @UserIdInt int userId,
             boolean disableSystemAppVerificationForTests) {
+        requireNonNull(context, "context must not be null");
+
         final List<ServiceInfo> services = new ArrayList<>();
         final List<ResolveInfo> resolveInfos = new ArrayList<>();
 
@@ -268,15 +268,20 @@
 
         for (ResolveInfo resolveInfo : resolveInfos) {
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            if (disableSystemAppVerificationForTests) {
+                if (serviceInfo != null) {
+                    services.add(serviceInfo);
+                }
+                continue;
+            }
+
             try {
-                PackageManager.ApplicationInfoFlags appInfoFlags =
-                        disableSystemAppVerificationForTests
-                                ? PackageManager.ApplicationInfoFlags.of(0)
-                                : PackageManager.ApplicationInfoFlags.of(
-                                        PackageManager.MATCH_SYSTEM_ONLY);
                 ApplicationInfo appInfo =
                         context.getPackageManager()
-                                .getApplicationInfo(serviceInfo.packageName, appInfoFlags);
+                                .getApplicationInfo(
+                                        serviceInfo.packageName,
+                                        PackageManager.ApplicationInfoFlags.of(
+                                                PackageManager.MATCH_SYSTEM_ONLY));
 
                 if (appInfo == null || serviceInfo == null) {
                     continue;
@@ -300,19 +305,22 @@
     public static List<CredentialProviderInfo> getAvailableSystemServices(
             @NonNull Context context,
             @UserIdInt int userId,
-            boolean disableSystemAppVerificationForTests) {
+            boolean disableSystemAppVerificationForTests,
+            Set<ServiceInfo> enabledServices) {
         requireNonNull(context, "context must not be null");
+
         final List<CredentialProviderInfo> providerInfos = new ArrayList<>();
         for (ServiceInfo si :
                 getAvailableSystemServiceInfos(
                         context, userId, disableSystemAppVerificationForTests)) {
             try {
                 CredentialProviderInfo cpi =
-                        new CredentialProviderInfo(
+                        CredentialProviderInfoFactory.create(
                                 context,
                                 si,
                                 /* isSystemProvider= */ true,
-                                disableSystemAppVerificationForTests);
+                                disableSystemAppVerificationForTests,
+                                enabledServices.contains(si));
                 if (cpi.isSystemProvider()) {
                     providerInfos.add(cpi);
                 } else {
@@ -325,45 +333,12 @@
         return providerInfos;
     }
 
-    /**
-     * Returns true if the service supports the given {@code credentialType}, false otherwise.
-     */
-    @NonNull
-    public boolean hasCapability(@NonNull String credentialType) {
-        return mCapabilities.contains(credentialType);
-    }
+    private static @Nullable PackagePolicy getDeviceManagerPolicy(
+            @NonNull Context context, int userId) {
+        Context newContext = context.createContextAsUser(UserHandle.of(userId), 0);
 
-    /** Returns the service info. */
-    @NonNull
-    public ServiceInfo getServiceInfo() {
-        return mServiceInfo;
-    }
-
-    public boolean isSystemProvider() {
-        return mIsSystemProvider;
-    }
-
-    /** Returns the service icon. */
-    @Nullable
-    public Drawable getServiceIcon() {
-        return mIcon;
-    }
-
-    /** Returns the service label. */
-    @Nullable
-    public CharSequence getServiceLabel() {
-        return mLabel;
-    }
-
-    /** Returns an immutable list of capabilities this provider service can support. */
-    @NonNull
-    public List<String> getCapabilities() {
-        return Collections.unmodifiableList(mCapabilities);
-    }
-
-    private static @Nullable PackagePolicy getDeviceManagerPolicy(@NonNull Context context) {
         try {
-            DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+            DevicePolicyManager dpm = newContext.getSystemService(DevicePolicyManager.class);
             return dpm.getCredentialManagerPolicy();
         } catch (SecurityException e) {
             // If the current user is not enrolled in DPM then this can throw a security error.
@@ -381,21 +356,53 @@
     public static List<CredentialProviderInfo> getCredentialProviderServices(
             @NonNull Context context,
             int userId,
-            boolean disableSystemAppVerificationForTests,
-            int providerFilter) {
+            int providerFilter,
+            Set<ServiceInfo> enabledServices) {
         requireNonNull(context, "context must not be null");
 
         // Get the device policy.
-        PackagePolicy pp = getDeviceManagerPolicy(context);
+        PackagePolicy pp = getDeviceManagerPolicy(context, userId);
 
         // Generate the provider list.
+        final boolean disableSystemAppVerificationForTests = false;
         ProviderGenerator generator =
                 new ProviderGenerator(
                         context, pp, disableSystemAppVerificationForTests, providerFilter);
         generator.addUserProviders(
-                getUserProviders(context, userId, disableSystemAppVerificationForTests));
+                getUserProviders(
+                        context, userId, disableSystemAppVerificationForTests, enabledServices));
         generator.addSystemProviders(
-                getAvailableSystemServices(context, userId, disableSystemAppVerificationForTests));
+                getAvailableSystemServices(
+                        context, userId, disableSystemAppVerificationForTests, enabledServices));
+        return generator.getProviders();
+    }
+
+    /**
+     * Returns the valid credential provider services available for the user with the given {@code
+     * userId}. Includes test providers.
+     */
+    @NonNull
+    public static List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
+            @NonNull Context context,
+            int userId,
+            int providerFilter,
+            Set<ServiceInfo> enabledServices) {
+        requireNonNull(context, "context must not be null");
+
+        // Get the device policy.
+        PackagePolicy pp = getDeviceManagerPolicy(context, userId);
+
+        // Generate the provider list.
+        final boolean disableSystemAppVerificationForTests = true;
+        ProviderGenerator generator =
+                new ProviderGenerator(
+                        context, pp, disableSystemAppVerificationForTests, providerFilter);
+        generator.addUserProviders(
+                getUserProviders(
+                        context, userId, disableSystemAppVerificationForTests, enabledServices));
+        generator.addSystemProviders(
+                getAvailableSystemServices(
+                        context, userId, disableSystemAppVerificationForTests, enabledServices));
         return generator.getProviders();
     }
 
@@ -484,7 +491,8 @@
     private static List<CredentialProviderInfo> getUserProviders(
             @NonNull Context context,
             @UserIdInt int userId,
-            boolean disableSystemAppVerificationForTests) {
+            boolean disableSystemAppVerificationForTests,
+            Set<ServiceInfo> enabledServices) {
         final List<CredentialProviderInfo> services = new ArrayList<>();
         final List<ResolveInfo> resolveInfos =
                 context.getPackageManager()
@@ -496,11 +504,12 @@
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
             try {
                 CredentialProviderInfo cpi =
-                        new CredentialProviderInfo(
+                        CredentialProviderInfoFactory.create(
                                 context,
                                 serviceInfo,
                                 /* isSystemProvider= */ false,
-                                disableSystemAppVerificationForTests);
+                                disableSystemAppVerificationForTests,
+                                enabledServices.contains(serviceInfo));
                 if (!cpi.isSystemProvider()) {
                     services.add(cpi);
                 }
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 94384b0..f74f533 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -533,7 +533,7 @@
      *     the calling package or if the calling user cannot act on behalf of the user from the
      *     {@code context}.</li>
      *     <li> {@link IllegalArgumentException} if the user of the {@code context} is not the
-     *     current user.</li>
+     *     current user. Only thrown for apps targeting {@link Build.VERSION_CODES#TIRAMISU}</li>
      * </ul>
      */
     public static final void requestListeningState(Context context, ComponentName component) {
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2ae882c..6201b3a 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -220,9 +220,9 @@
         DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
         DEFAULT_FLAGS.put(SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, "true");
         DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false");
-        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "false");
-        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY, "false");
-        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD, "false");
+        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "true");
+        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY, "true");
+        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD, "true");
         DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE, "false");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_PHASE2, "false");
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 406f446..24a0355 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -244,6 +244,10 @@
         window = iwindow;
     }
 
+    public @Nullable IBinder getWindowToken() {
+        return windowToken;
+    }
+
     public IWindow getWindow() {
         if (window != null) {
             return window;
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 85aea85..4a7ed64 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -32,9 +32,10 @@
  *
  * Use {@link #obtain} to retrieve a new instance of the class when you are going
  * to begin tracking.  Put the motion events you receive into it with
- * {@link #addMovement(MotionEvent)}.  When you want to determine the velocity call
- * {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)}
- * and {@link #getYVelocity(int)} to retrieve the velocity for each pointer id.
+ * {@link #addMovement(MotionEvent)}.  When you want to determine the velocity, call
+ * {@link #computeCurrentVelocity(int)} and then call the velocity-getter methods like
+ * {@link #getXVelocity(int)}, {@link #getYVelocity(int)}, or {@link #getAxisVelocity(int, int)}
+ * to retrieve velocity for different axes and/or pointer IDs.
  */
 public final class VelocityTracker {
     private static final SynchronizedPool<VelocityTracker> sPool =
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 869efc69..8bf3232 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8920,7 +8920,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
+    @TestApi
+    public void getBoundsOnScreen(@NonNull Rect outRect, boolean clipToParent) {
         if (mAttachInfo == null) {
             return;
         }
@@ -8928,6 +8929,9 @@
         getBoundsToScreenInternal(position, clipToParent);
         outRect.set(Math.round(position.left), Math.round(position.top),
                 Math.round(position.right), Math.round(position.bottom));
+        // If "Sandboxing View Bounds APIs" override is enabled, applyViewBoundsSandboxingIfNeeded
+        // will sandbox outRect within window bounds.
+        mAttachInfo.mViewRootImpl.applyViewBoundsSandboxingIfNeeded(outRect);
     }
 
     /**
@@ -16159,7 +16163,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public void getWindowDisplayFrame(Rect outRect) {
+    @TestApi
+    public void getWindowDisplayFrame(@NonNull Rect outRect) {
         if (mAttachInfo != null) {
             mAttachInfo.mViewRootImpl.getDisplayFrame(outRect);
             return;
@@ -26375,6 +26380,9 @@
         if (info != null) {
             outLocation[0] += info.mWindowLeft;
             outLocation[1] += info.mWindowTop;
+            // If OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS override is enabled,
+            // applyViewLocationSandboxingIfNeeded sandboxes outLocation within window bounds.
+            info.mViewRootImpl.applyViewLocationSandboxingIfNeeded(outLocation);
         }
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 807af5b..a8b4da1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS;
 import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
 import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
 import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
@@ -79,6 +80,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
@@ -92,12 +94,14 @@
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.Size;
 import android.annotation.UiContext;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ICompatCameraControlCallback;
 import android.app.ResourcesManager;
 import android.app.WindowConfiguration;
+import android.app.compat.CompatChanges;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -895,6 +899,15 @@
 
     private boolean mRelayoutRequested;
 
+    /**
+     * Whether sandboxing of {@link android.view.View#getBoundsOnScreen},
+     * {@link android.view.View#getLocationOnScreen(int[])},
+     * {@link android.view.View#getWindowDisplayFrame} and
+     * {@link android.view.View#getWindowVisibleDisplayFrame}
+     * within Activity bounds is enabled for the current application.
+     */
+    private final boolean mViewBoundsSandboxingEnabled;
+
     private int mLastTransformHint = Integer.MIN_VALUE;
 
     private AccessibilityWindowAttributes mAccessibilityWindowAttributes;
@@ -986,6 +999,8 @@
                 mViewConfiguration,
                 mContext.getSystemService(InputMethodManager.class));
 
+        mViewBoundsSandboxingEnabled = getViewBoundsSandboxingEnabled();
+
         String processorOverrideName = context.getResources().getString(
                                     R.string.config_inputEventCompatProcessorOverrideClassName);
         if (processorOverrideName.isEmpty()) {
@@ -8598,6 +8613,9 @@
      */
     void getDisplayFrame(Rect outFrame) {
         outFrame.set(mTmpFrames.displayFrame);
+        // Apply sandboxing here (in getter) due to possible layout updates on the client after
+        // mTmpFrames.displayFrame is received from the server.
+        applyViewBoundsSandboxingIfNeeded(outFrame);
     }
 
     /**
@@ -8614,6 +8632,69 @@
         outFrame.top += insets.top;
         outFrame.right -= insets.right;
         outFrame.bottom -= insets.bottom;
+        // Apply sandboxing here (in getter) due to possible layout updates on the client after
+        // mTmpFrames.displayFrame is received from the server.
+        applyViewBoundsSandboxingIfNeeded(outFrame);
+    }
+
+    /**
+     * Offset outRect to make it sandboxed within Window's bounds.
+     *
+     * <p>This is used by {@link android.view.View#getBoundsOnScreen},
+     * {@link android.view.ViewRootImpl#getDisplayFrame} and
+     * {@link android.view.ViewRootImpl#getWindowVisibleDisplayFrame}, which are invoked by
+     * {@link android.view.View#getWindowDisplayFrame} and
+     * {@link android.view.View#getWindowVisibleDisplayFrame}, as well as
+     * {@link android.view.ViewDebug#captureLayers} for debugging.
+     */
+    void applyViewBoundsSandboxingIfNeeded(final Rect inOutRect) {
+        if (mViewBoundsSandboxingEnabled) {
+            final Rect bounds = getConfiguration().windowConfiguration.getBounds();
+            inOutRect.offset(-bounds.left, -bounds.top);
+        }
+    }
+
+    /**
+     * Offset outLocation to make it sandboxed within Window's bounds.
+     *
+     * <p>This is used by {@link android.view.View#getLocationOnScreen(int[])}
+     */
+    public void applyViewLocationSandboxingIfNeeded(@Size(2) int[] outLocation) {
+        if (mViewBoundsSandboxingEnabled) {
+            final Rect bounds = getConfiguration().windowConfiguration.getBounds();
+            outLocation[0] -= bounds.left;
+            outLocation[1] -= bounds.top;
+        }
+    }
+
+    private boolean getViewBoundsSandboxingEnabled() {
+        // System dialogs (e.g. ANR) can be created within System process, so handleBindApplication
+        // may be never called. This results into all app compat changes being enabled
+        // (see b/268007823) because AppCompatCallbacks.install() is never called with non-empty
+        // array.
+        // With ActivityThread.isSystem we verify that it is not the system process,
+        // then this CompatChange can take effect.
+        if (ActivityThread.isSystem()
+                || !CompatChanges.isChangeEnabled(OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS)) {
+            // It is a system process or OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS change-id is disabled.
+            return false;
+        }
+
+        // OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS is enabled by the device manufacturer.
+        try {
+            final List<PackageManager.Property> properties = mContext.getPackageManager()
+                    .queryApplicationProperty(PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS);
+
+            final boolean isOptedOut = !properties.isEmpty() && !properties.get(0).getBoolean();
+            if (isOptedOut) {
+                // PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS is disabled by the app devs.
+                return false;
+            }
+        } catch (RuntimeException e) {
+            // remote exception.
+        }
+
+        return true;
     }
 
     /**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index dfb11bc..35ed88f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -872,6 +872,42 @@
 
     /**
      * Application level {@link android.content.pm.PackageManager.Property PackageManager
+     * .Property} for an app to inform the system that it needs to be opted-out from the
+     * compatibility treatment that sandboxes {@link android.view.View} API.
+     *
+     * <p>The treatment can be enabled by device manufacturers for applications which misuse
+     * {@link android.view.View} APIs by expecting that
+     * {@link android.view.View#getLocationOnScreen},
+     * {@link android.view.View#getBoundsOnScreen},
+     * {@link android.view.View#getWindowVisibleDisplayFrame},
+     * {@link android.view.View#getWindowDisplayFrame}
+     * return coordinates as if an activity is positioned in the top-left corner of the screen, with
+     * left coordinate equal to 0. This may not be the case for applications in multi-window and in
+     * letterbox modes.
+     *
+     * <p>Setting this property to {@code false} informs the system that the application must be
+     * opted-out from the "Sandbox {@link android.view.View} API to Activity bounds" treatment even
+     * if the device manufacturer has opted the app into the treatment.
+     *
+     * <p>Not setting this property at all, or setting this property to {@code true} has no effect.
+     *
+     * <p><b>Syntax:</b>
+     * <pre>
+     * &lt;application&gt;
+     *   &lt;property
+     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS"
+     *     android:value="false"/&gt;
+     * &lt;/application&gt;
+     * </pre>
+     *
+     * @hide
+     */
+    // TODO(b/263984287): Make this public API.
+    String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS =
+            "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS";
+
+    /**
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
      * .Property} for an app to inform the system that the application can be opted-in or opted-out
      * from the compatibility treatment that enables sending a fake focus event for unfocused
      * resumed split screen activities. This is needed because some game engines wait to get
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 6ddfcb8..8cff066 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -562,11 +562,11 @@
         final int numberEvents = mEvents.size();
         final String reasonString = getFlushReasonAsString(reason);
 
-        if (sDebug) {
+        if (sVerbose) {
             ContentCaptureEvent event = mEvents.get(numberEvents - 1);
             String forceString = (reason == FLUSH_REASON_FORCE_FLUSH) ? ". The force flush event "
                     + ContentCaptureEvent.getTypeAsString(event.getType()) : "";
-            Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason)
+            Log.v(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason)
                     + forceString);
         }
         if (mFlushHistory != null) {
diff --git a/core/java/android/window/WindowInfosListener.java b/core/java/android/window/WindowInfosListener.java
index 8db5a5e..42bb674 100644
--- a/core/java/android/window/WindowInfosListener.java
+++ b/core/java/android/window/WindowInfosListener.java
@@ -16,6 +16,8 @@
 
 package android.window;
 
+import android.Manifest;
+import android.annotation.RequiresPermission;
 import android.graphics.Matrix;
 import android.util.Pair;
 import android.util.Size;
@@ -49,10 +51,13 @@
     /**
      * Register the WindowInfosListener.
      *
+     * Registering WindowInfosListeners should only be done within system_server and shell.
+     *
      * @return The cached values for InputWindowHandles and DisplayInfos. This is the last updated
      * value that was sent from SurfaceFlinger to this particular process. If there was nothing
      * registered previously, then the data can be empty.
      */
+    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
     public Pair<InputWindowHandle[], DisplayInfo[]> register() {
         return nativeRegister(mNativeListener);
     }
@@ -65,8 +70,12 @@
     }
 
     private static native long nativeCreate(WindowInfosListener thiz);
+
+    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
     private static native Pair<InputWindowHandle[], DisplayInfo[]> nativeRegister(long ptr);
+
     private static native void nativeUnregister(long ptr);
+
     private static native long nativeGetFinalizer();
 
     /**
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
new file mode 100644
index 0000000..429156f
--- /dev/null
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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 android.window;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.InputConfig;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.view.InputWindowHandle;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+
+/**
+ * Wrapper class to provide access to WindowInfosListener within tests.
+ *
+ * @hide
+ */
+@TestApi
+public class WindowInfosListenerForTest {
+
+    /**
+     * Window properties passed to {@code @WindowInfosListenerForTest#onWindowInfosChanged}.
+     */
+    public static class WindowInfo {
+        /**
+         * The window's token.
+         */
+        @NonNull
+        public final IBinder windowToken;
+
+        /**
+         * The window's name.
+         */
+        @NonNull
+        public final String name;
+
+        /**
+         * The window's position and size in display space.
+         */
+        @NonNull
+        public final Rect bounds;
+
+        WindowInfo(@NonNull IBinder windowToken, @NonNull String name, @NonNull Rect bounds) {
+            this.windowToken = windowToken;
+            this.name = name;
+            this.bounds = bounds;
+        }
+    }
+
+    private static final String TAG = "WindowInfosListenerForTest";
+
+    private ArrayMap<Consumer<List<WindowInfo>>, WindowInfosListener> mListeners;
+
+    public WindowInfosListenerForTest() {
+        mListeners = new ArrayMap<>();
+    }
+
+    /**
+     * Register a listener that is called when the system's list of visible windows has changes in
+     * position or visibility.
+     *
+     * @param consumer Consumer that is called with reverse Z ordered lists of WindowInfo instances
+     *                 where the first value is the topmost window.
+     */
+    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+    public void addWindowInfosListener(
+            @NonNull Consumer<List<WindowInfo>> consumer) {
+        var calledWithInitialState = new CountDownLatch(1);
+        var listener = new WindowInfosListener() {
+            @Override
+            public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
+                    DisplayInfo[] displayInfos) {
+                try {
+                    calledWithInitialState.await();
+                } catch (InterruptedException exception) {
+                    Log.e(TAG,
+                            "Exception thrown while waiting for listener to be called with "
+                                    + "initial state");
+                }
+                consumer.accept(buildWindowInfos(windowHandles));
+            }
+        };
+        mListeners.put(consumer, listener);
+        Pair<InputWindowHandle[], WindowInfosListener.DisplayInfo[]> initialState =
+                listener.register();
+        consumer.accept(buildWindowInfos(initialState.first));
+        calledWithInitialState.countDown();
+    }
+
+    /**
+     * Unregisters the listener.
+     */
+    public void removeWindowInfosListener(@NonNull Consumer<List<WindowInfo>> consumer) {
+        WindowInfosListener listener = mListeners.remove(consumer);
+        if (listener == null) {
+            return;
+        }
+        listener.unregister();
+    }
+
+    private static List<WindowInfo> buildWindowInfos(InputWindowHandle[] windowHandles) {
+        var windowInfos = new ArrayList<WindowInfo>(windowHandles.length);
+        for (var handle : windowHandles) {
+            if ((handle.inputConfig & InputConfig.NOT_VISIBLE) != 0) {
+                continue;
+            }
+            var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
+                    handle.frameBottom);
+            windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, bounds));
+        }
+        return windowInfos;
+    }
+}
diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
index 6c01780..2a9025d 100644
--- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -61,7 +61,7 @@
             final int size = mPendingRequests.size();
             if (mVerbose) Slog.v(mTag, "Sending " + size + " pending requests");
             for (int i = 0; i < size; i++) {
-                mPendingRequests.get(i).run();
+                handlePendingRequest(mPendingRequests.get(i));
             }
             mPendingRequests.clear();
         }
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index d5f7ba5..18414cf 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -107,7 +107,7 @@
     private int mServiceExitSubReason;
 
     /** Requests that have been scheduled, but that are not finished yet */
-    private final ArrayList<BasePendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>();
+    protected final ArrayList<BasePendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>();
 
     /**
      * Callback called when the service dies.
@@ -673,6 +673,11 @@
                 mCancelled = true;
             }
 
+            S service = mWeakService.get();
+            if (service != null) {
+                service.finishRequest(this);
+            }
+
             onCancel();
             return true;
         }
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 4bc567a..ad54004 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -20,20 +20,21 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <input/InputTransport.h>
+#include <inttypes.h>
 #include <log/log.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <utils/Looper.h>
+
+#include <optional>
+#include <unordered_map>
+
 #include "android_os_MessageQueue.h"
 #include "android_view_InputChannel.h"
 #include "android_view_KeyEvent.h"
 #include "android_view_MotionEvent.h"
 #include "core_jni_helpers.h"
 
-#include <inttypes.h>
-#include <unordered_map>
-
-
 using android::base::Result;
 
 namespace android {
@@ -67,7 +68,7 @@
     jobject mSenderWeakGlobal;
     InputPublisher mInputPublisher;
     sp<MessageQueue> mMessageQueue;
-    std::unordered_map<uint32_t, uint32_t> mPublishedSeqMap;
+    std::unordered_map<uint32_t, std::optional<uint32_t>> mPublishedSeqMap;
 
     uint32_t mNextPublishedSeq;
 
@@ -165,8 +166,14 @@
                     getInputChannelName().c_str(), status);
             return status;
         }
+        // mPublishedSeqMap tracks all sequences published from this sender. Only the last
+        // sequence number is used to signal this motion event is finished.
+        if (i == event->getHistorySize()) {
+            mPublishedSeqMap.emplace(publishedSeq, seq);
+        } else {
+            mPublishedSeqMap.emplace(publishedSeq, std::nullopt);
+        }
     }
-    mPublishedSeqMap.emplace(publishedSeq, seq);
     return OK;
 }
 
@@ -277,8 +284,16 @@
         // does something wrong and sends bad data. Just ignore and process other events.
         return true;
     }
-    const uint32_t seq = it->second;
+
+    const std::optional<uint32_t> seqOptional = it->second;
     mPublishedSeqMap.erase(it);
+    // If this optional does not have a value, it means we are processing an event that had history
+    // and was split. There are more events coming, so we can't call 'dispatchInputEventFinished'
+    // yet. The final split event will have a valid sequence number.
+    if (!seqOptional.has_value()) {
+        return true;
+    }
+    const uint32_t seq = seqOptional.value();
 
     if (kDebugDispatchCycle) {
         ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.",
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 146ac9d..7503dde4 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -115,6 +115,9 @@
         optional SettingProto sound_cache = 2;
         optional SettingProto light_pulse = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto vibration_intensity = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto camera_flash_notification = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto screen_flash_notification = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto screen_flash_notification_color_global = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Notification notification = 17;
 
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 3c4bac8..b71995f 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -178,3 +178,12 @@
         " > $(out)",
     tools: ["xmllint"],
 }
+
+filegroup {
+    name: "frameworks-base-core-AndroidManifest.xml",
+    srcs: ["AndroidManifest.xml"],
+    visibility: [
+        "//frameworks/base",
+        "//frameworks/base/api",
+    ],
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5a0ac0f..cb6c092 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -822,6 +822,8 @@
     <protected-broadcast android:name="com.android.internal.telephony.cat.SMS_SENT_ACTION" />
     <protected-broadcast android:name="com.android.internal.telephony.cat.SMS_DELIVERY_ACTION" />
     <protected-broadcast android:name="android.companion.virtual.action.VIRTUAL_DEVICE_REMOVED" />
+    <protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_START_PREVIEW" />
+    <protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_STOP_PREVIEW" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
deleted file mode 100644
index 17b064c..0000000
--- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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 android.companion.virtual.sensor;
-
-import static android.hardware.Sensor.TYPE_ACCELEROMETER;
-import static android.hardware.SensorDirectChannel.RATE_STOP;
-import static android.hardware.SensorDirectChannel.RATE_VERY_FAST;
-import static android.hardware.SensorDirectChannel.TYPE_HARDWARE_BUFFER;
-import static android.hardware.SensorDirectChannel.TYPE_MEMORY_FILE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.hardware.Sensor;
-import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class VirtualSensorConfigTest {
-
-    private static final String SENSOR_NAME = "VirtualSensorName";
-    private static final String SENSOR_VENDOR = "VirtualSensorVendor";
-
-    @Test
-    public void parcelAndUnparcel_matches() {
-        final VirtualSensorConfig originalConfig =
-                new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
-                        .setVendor(SENSOR_VENDOR)
-                        .setHighestDirectReportRateLevel(RATE_VERY_FAST)
-                        .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
-                        .build();
-        final Parcel parcel = Parcel.obtain();
-        originalConfig.writeToParcel(parcel, /* flags= */ 0);
-        parcel.setDataPosition(0);
-        final VirtualSensorConfig recreatedConfig =
-                VirtualSensorConfig.CREATOR.createFromParcel(parcel);
-        assertThat(recreatedConfig.getType()).isEqualTo(originalConfig.getType());
-        assertThat(recreatedConfig.getName()).isEqualTo(originalConfig.getName());
-        assertThat(recreatedConfig.getVendor()).isEqualTo(originalConfig.getVendor());
-        assertThat(recreatedConfig.getHighestDirectReportRateLevel()).isEqualTo(RATE_VERY_FAST);
-        assertThat(recreatedConfig.getDirectChannelTypesSupported()).isEqualTo(TYPE_MEMORY_FILE);
-        // From hardware/libhardware/include/hardware/sensors-base.h:
-        //   0x400 is SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM (i.e. TYPE_MEMORY_FILE)
-        //   0x800 is SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC (i.e. TYPE_HARDWARE_BUFFER)
-        //   7 is SENSOR_FLAG_SHIFT_DIRECT_REPORT
-        assertThat(recreatedConfig.getFlags()).isEqualTo(0x400 | RATE_VERY_FAST << 7);
-    }
-
-    @Test
-    public void virtualSensorConfig_invalidName_throwsException() {
-        assertThrows(
-                NullPointerException.class,
-                () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, null));
-    }
-
-    @Test
-    public void virtualSensorConfig_invalidType_throwsException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> new VirtualSensorConfig.Builder(Sensor.TYPE_ALL, SENSOR_NAME));
-
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> new VirtualSensorConfig.Builder(0, SENSOR_NAME));
-    }
-
-    @Test
-    public void hardwareBufferDirectChannelTypeSupported_throwsException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
-                        .setDirectChannelTypesSupported(TYPE_HARDWARE_BUFFER | TYPE_MEMORY_FILE));
-    }
-
-    @Test
-    public void directChannelTypeSupported_missingHighestReportRateLevel_throwsException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
-                        .setDirectChannelTypesSupported(TYPE_MEMORY_FILE)
-                        .build());
-    }
-
-    @Test
-    public void directChannelTypeSupported_missingDirectChannelTypeSupported_throwsException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
-                        .setHighestDirectReportRateLevel(RATE_VERY_FAST)
-                        .build());
-    }
-
-    @Test
-    public void sensorConfig_onlyRequiredFields() {
-        final VirtualSensorConfig config =
-                new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME).build();
-        assertThat(config.getVendor()).isNull();
-        assertThat(config.getHighestDirectReportRateLevel()).isEqualTo(RATE_STOP);
-        assertThat(config.getDirectChannelTypesSupported()).isEqualTo(0);
-        assertThat(config.getFlags()).isEqualTo(0);
-    }
-}
diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorEventTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorEventTest.java
deleted file mode 100644
index c260ef9..0000000
--- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorEventTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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 android.companion.virtual.sensor;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.os.Parcel;
-import android.os.SystemClock;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class VirtualSensorEventTest {
-
-    private static final long TIMESTAMP_NANOS = SystemClock.elapsedRealtimeNanos();
-    private static final float[] SENSOR_VALUES = new float[] {1.2f, 3.4f, 5.6f};
-
-    @Test
-    public void parcelAndUnparcel_matches() {
-        final VirtualSensorEvent originalEvent = new VirtualSensorEvent.Builder(SENSOR_VALUES)
-                .setTimestampNanos(TIMESTAMP_NANOS)
-                .build();
-        final Parcel parcel = Parcel.obtain();
-        originalEvent.writeToParcel(parcel, /* flags= */ 0);
-        parcel.setDataPosition(0);
-        final VirtualSensorEvent recreatedEvent =
-                VirtualSensorEvent.CREATOR.createFromParcel(parcel);
-        assertThat(recreatedEvent.getValues()).isEqualTo(originalEvent.getValues());
-        assertThat(recreatedEvent.getTimestampNanos()).isEqualTo(originalEvent.getTimestampNanos());
-    }
-
-    @Test
-    public void sensorEvent_nullValues() {
-        assertThrows(
-                IllegalArgumentException.class, () -> new VirtualSensorEvent.Builder(null).build());
-    }
-
-    @Test
-    public void sensorEvent_noValues() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> new VirtualSensorEvent.Builder(new float[0]).build());
-    }
-
-    @Test
-    public void sensorEvent_noTimestamp_usesCurrentTime() {
-        final VirtualSensorEvent event = new VirtualSensorEvent.Builder(SENSOR_VALUES).build();
-        assertThat(event.getValues()).isEqualTo(SENSOR_VALUES);
-        assertThat(TIMESTAMP_NANOS).isLessThan(event.getTimestampNanos());
-        assertThat(event.getTimestampNanos()).isLessThan(SystemClock.elapsedRealtimeNanos());
-    }
-
-    @Test
-    public void sensorEvent_created() {
-        final VirtualSensorEvent event = new VirtualSensorEvent.Builder(SENSOR_VALUES)
-                .setTimestampNanos(TIMESTAMP_NANOS)
-                .build();
-        assertThat(event.getTimestampNanos()).isEqualTo(TIMESTAMP_NANOS);
-        assertThat(event.getValues()).isEqualTo(SENSOR_VALUES);
-    }
-}
diff --git a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
index 43334ab..e31d5ae 100644
--- a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
+++ b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
@@ -30,6 +30,7 @@
 import android.app.Activity;
 import android.app.slice.Slice;
 import android.content.Context;
+import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -47,6 +48,7 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -59,6 +61,17 @@
     @Mock
     private Activity mMockActivity;
 
+    private static final int TEST_USER_ID = 1;
+    private static final CredentialProviderInfo TEST_CREDENTIAL_PROVIDER_INFO =
+                new CredentialProviderInfo.Builder(new ServiceInfo())
+                        .setSystemProvider(true)
+                        .setOverrideLabel("test")
+                        .addCapabilities(Arrays.asList("passkey"))
+                        .setEnabled(true)
+                        .build();
+    private static final List<CredentialProviderInfo> TEST_CREDENTIAL_PROVIDER_INFO_LIST =
+                Arrays.asList(TEST_CREDENTIAL_PROVIDER_INFO);
+
     private GetCredentialRequest mGetRequest;
     private CreateCredentialRequest mCreateRequest;
 
@@ -438,95 +451,53 @@
     }
 
     @Test
-    public void testListEnabledProviders_nullExecutor() {
-        assertThrows(NullPointerException.class,
-                () -> mCredentialManager.listEnabledProviders(null, null, result -> {
-                }));
-
+    public void testGetCredentialProviderServices_allProviders() throws RemoteException {
+        verifyGetCredentialProviderServices(CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS);
     }
 
     @Test
-    public void testListEnabledProviders_nullCallback() {
-        assertThrows(NullPointerException.class,
-                () -> mCredentialManager.listEnabledProviders(null, mExecutor, null));
-
+    public void testGetCredentialProviderServices_userProviders() throws RemoteException {
+        verifyGetCredentialProviderServices(CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY);
     }
 
     @Test
-    public void testListEnabledProviders_alreadyCancelled() throws RemoteException {
-        final CancellationSignal cancellation = new CancellationSignal();
-        cancellation.cancel();
-
-        mCredentialManager.listEnabledProviders(cancellation, mExecutor, result -> {
-        });
-
-        verify(mMockCredentialManagerService, never()).listEnabledProviders(any());
+    public void testGetCredentialProviderServices_systemProviders() throws RemoteException {
+        verifyGetCredentialProviderServices(CredentialManager.PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY);
     }
 
     @Test
-    public void testListEnabledProviders_cancel() throws RemoteException {
-        final ICancellationSignal serviceSignal = mock(ICancellationSignal.class);
-        final CancellationSignal cancellation = new CancellationSignal();
-
-        OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
-                mock(OutcomeReceiver.class);
-
-        when(mMockCredentialManagerService.listEnabledProviders(any())).thenReturn(serviceSignal);
-
-        mCredentialManager.listEnabledProviders(cancellation, mExecutor, callback);
-
-        verify(mMockCredentialManagerService).listEnabledProviders(any());
-
-        cancellation.cancel();
-        verify(serviceSignal).cancel();
+    public void testGetCredentialProviderServicesForTesting_allProviders() throws RemoteException {
+        verifyGetCredentialProviderServicesForTesting(CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS);
     }
 
     @Test
-    public void testListEnabledProviders_failed() throws RemoteException {
-        ArgumentCaptor<IListEnabledProvidersCallback> callbackCaptor = ArgumentCaptor.forClass(
-                IListEnabledProvidersCallback.class);
-        ArgumentCaptor<ListEnabledProvidersException> errorCaptor = ArgumentCaptor.forClass(
-                ListEnabledProvidersException.class);
-
-        OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
-                mock(OutcomeReceiver.class);
-
-        when(mMockCredentialManagerService.listEnabledProviders(
-                callbackCaptor.capture())).thenReturn(mock(ICancellationSignal.class));
-        mCredentialManager.listEnabledProviders(null, mExecutor, callback);
-        verify(mMockCredentialManagerService).listEnabledProviders(any());
-
-        final String errorType = "type";
-        callbackCaptor.getValue().onError("type", "unknown error");
-        verify(callback).onError(errorCaptor.capture());
-
-        assertThat(errorCaptor.getValue().getType()).isEqualTo(errorType);
+    public void testGetCredentialProviderServicesForTesting_userProviders() throws RemoteException {
+        verifyGetCredentialProviderServicesForTesting(CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY);
     }
 
     @Test
-    public void testListEnabledProviders_success() throws RemoteException {
-        ListEnabledProvidersResponse response = ListEnabledProvidersResponse.create(
-                List.of("foo", "bar", "baz"));
+    public void testGetCredentialProviderServicesForTesting_systemProviders() throws RemoteException {
+        verifyGetCredentialProviderServicesForTesting(CredentialManager.PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY);
+    }
 
-        OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
-                mock(OutcomeReceiver.class);
+    private void verifyGetCredentialProviderServices(int testFilter) throws RemoteException {
+        when(mMockCredentialManagerService.getCredentialProviderServices(
+                TEST_USER_ID, testFilter)).thenReturn(TEST_CREDENTIAL_PROVIDER_INFO_LIST);
 
-        ArgumentCaptor<IListEnabledProvidersCallback> callbackCaptor = ArgumentCaptor.forClass(
-                IListEnabledProvidersCallback.class);
-        ArgumentCaptor<ListEnabledProvidersResponse> responseCaptor = ArgumentCaptor.forClass(
-                ListEnabledProvidersResponse.class);
+        List<CredentialProviderInfo> output =
+                mCredentialManager.getCredentialProviderServices(TEST_USER_ID, testFilter);
 
-        when(mMockCredentialManagerService.listEnabledProviders(
-                callbackCaptor.capture())).thenReturn(mock(ICancellationSignal.class));
-        mCredentialManager.listEnabledProviders(null, mExecutor, callback);
+        assertThat(output).containsExactlyElementsIn(TEST_CREDENTIAL_PROVIDER_INFO_LIST);
+    }
 
-        verify(mMockCredentialManagerService).listEnabledProviders(any());
+    private void verifyGetCredentialProviderServicesForTesting(int testFilter) throws RemoteException {
+        when(mMockCredentialManagerService.getCredentialProviderServicesForTesting(
+                testFilter)).thenReturn(TEST_CREDENTIAL_PROVIDER_INFO_LIST);
 
-        callbackCaptor.getValue().onResponse(response);
+        List<CredentialProviderInfo> output =
+                mCredentialManager.getCredentialProviderServicesForTesting(testFilter);
 
-        verify(callback).onResult(responseCaptor.capture());
-        assertThat(responseCaptor.getValue().getProviderComponentNames()).containsExactlyElementsIn(
-                response.getProviderComponentNames());
+        assertThat(output).containsExactlyElementsIn(TEST_CREDENTIAL_PROVIDER_INFO_LIST);
     }
 
     @Test
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index feedb7d..9ac84a6 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -25,13 +25,11 @@
 
 /**
  * Gainmap represents a mechanism for augmenting an SDR image to produce an HDR one with variable
- * display adjustment capability.
- *
- * It is a combination of a set of metadata describing how to apply the gainmap, as well as either
- * a 1 (such as {@link android.graphics.Bitmap.Config#ALPHA_8} or 3
+ * display adjustment capability. It is a combination of a set of metadata describing how to apply
+ * the gainmap, as well as either a 1 (such as {@link android.graphics.Bitmap.Config#ALPHA_8} or 3
  * (such as {@link android.graphics.Bitmap.Config#ARGB_8888} with the alpha channel ignored)
  * channel Bitmap that represents the gainmap data itself.
- *
+ * <p>
  * When rendering to an {@link android.content.pm.ActivityInfo#COLOR_MODE_HDR} activity, the
  * hardware accelerated {@link Canvas} will automatically apply the gainmap when sufficient
  * HDR headroom is available.
@@ -45,7 +43,7 @@
  * image, often at a lower resolution (such as 1/4th), along with some metadata to describe
  * how to apply the gainmap. The gainmap image itself is then a greyscale image representing
  * the transformation to apply onto the base image to reconstruct an HDR rendition of it.
- *
+ * <p>
  * As such these "gainmap images" consist of 3 parts - a base {@link Bitmap} with a
  * {@link Bitmap#getGainmap()} that returns an instance of this class which in turn contains
  * the enhancement layer represented as another Bitmap, accessible via {@link #getGainmapContents()}
@@ -55,25 +53,27 @@
  * When doing custom rendering such as to an OpenGL ES or Vulkan context, the gainmap is not
  * automatically applied. In such situations, the following steps are appropriate to render the
  * gainmap in combination with the base image.
- *
+ * <p>
  * Suppose our display has HDR to SDR ratio of H, and we wish to display an image with gainmap on
  * this display. Let B be the pixel value from the base image in a color space that has the
  * primaries of the base image and a linear transfer function. Let G be the pixel value from the
  * gainmap. Let D be the output pixel in the same color space as B. The value of D is computed
  * as follows:
- *
+ * <p>
  * First, let W be a weight parameter determining how much the gainmap will be applied.
+ * <pre class="prettyprint">
  *   W = clamp((log(H)                      - log(minDisplayRatioForHdrTransition)) /
- *             (log(displayRatioForFullHdr) - log(minDisplayRatioForHdrTransition), 0, 1)
+ *             (log(displayRatioForFullHdr) - log(minDisplayRatioForHdrTransition), 0, 1)</pre>
  *
  * Next, let L be the gainmap value in log space. We compute this from the value G that was
  * sampled from the texture as follows:
- *   L = mix(log(ratioMin), log(ratioMax), pow(G, gamma))
- *
+ * <pre class="prettyprint">
+ *   L = mix(log(ratioMin), log(ratioMax), pow(G, gamma))</pre>
  * Finally, apply the gainmap to compute D, the displayed pixel. If the base image is SDR then
  * compute:
- *   D = (B + epsilonSdr) * exp(L * W) - epsilonHdr
- *
+ * <pre class="prettyprint">
+ *   D = (B + epsilonSdr) * exp(L * W) - epsilonHdr</pre>
+ * <p>
  * In the above math, log() is a natural logarithm and exp() is natural exponentiation. The base
  * for these functions cancels out and does not affect the result, so other bases may be used
  * if preferred.
@@ -150,7 +150,6 @@
     /**
      * Sets the gainmap ratio min. For single-plane gainmaps, r, g, and b should be the same.
      */
-    @NonNull
     public void setRatioMin(float r, float g, float b) {
         nSetRatioMin(mNativePtr, r, g, b);
     }
@@ -169,7 +168,6 @@
     /**
      * Sets the gainmap ratio max. For single-plane gainmaps, r, g, and b should be the same.
      */
-    @NonNull
     public void setRatioMax(float r, float g, float b) {
         nSetRatioMax(mNativePtr, r, g, b);
     }
@@ -188,7 +186,6 @@
     /**
      * Sets the gainmap gamma. For single-plane gainmaps, r, g, and b should be the same.
      */
-    @NonNull
     public void setGamma(float r, float g, float b) {
         nSetGamma(mNativePtr, r, g, b);
     }
@@ -208,7 +205,6 @@
      * Sets the sdr epsilon which is used to avoid numerical instability.
      * For single-plane gainmaps, r, g, and b should be the same.
      */
-    @NonNull
     public void setEpsilonSdr(float r, float g, float b) {
         nSetEpsilonSdr(mNativePtr, r, g, b);
     }
@@ -228,7 +224,6 @@
      * Sets the hdr epsilon which is used to avoid numerical instability.
      * For single-plane gainmaps, r, g, and b should be the same.
      */
-    @NonNull
     public void setEpsilonHdr(float r, float g, float b) {
         nSetEpsilonHdr(mNativePtr, r, g, b);
     }
@@ -248,8 +243,7 @@
      * Sets the hdr/sdr ratio at which point the gainmap is fully applied.
      * @param max The hdr/sdr ratio at which the gainmap is fully applied. Must be >= 1.0f
      */
-    @NonNull
-    public void setDisplayRatioForFullHdr(float max) {
+    public void setDisplayRatioForFullHdr(@FloatRange(from = 1.0f) float max) {
         if (!Float.isFinite(max) || max < 1f) {
             throw new IllegalArgumentException(
                     "setDisplayRatioForFullHdr must be >= 1.0f, got = " + max);
@@ -269,7 +263,6 @@
      * Sets the hdr/sdr ratio below which only the SDR image is displayed.
      * @param min The minimum hdr/sdr ratio at which to begin applying the gainmap. Must be >= 1.0f
      */
-    @NonNull
     public void setMinDisplayRatioForHdrTransition(@FloatRange(from = 1.0f) float min) {
         if (!Float.isFinite(min) || min < 1f) {
             throw new IllegalArgumentException(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index 480bf93..53bf42a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -39,6 +39,9 @@
  * Represents the content overlay used during the entering PiP animation.
  */
 public abstract class PipContentOverlay {
+    // Fixed string used in WMShellFlickerTests
+    protected static final String LAYER_NAME = "PipContentOverlay";
+
     protected SurfaceControl mLeash;
 
     /** Attaches the internal {@link #mLeash} to the given parent leash. */
@@ -86,7 +89,7 @@
             mContext = context;
             mLeash = new SurfaceControl.Builder(new SurfaceSession())
                     .setCallsite(TAG)
-                    .setName(TAG)
+                    .setName(LAYER_NAME)
                     .setColorLayer()
                     .build();
         }
@@ -139,7 +142,7 @@
             mSourceRectHint = new Rect(sourceRectHint);
             mLeash = new SurfaceControl.Builder(new SurfaceSession())
                     .setCallsite(TAG)
-                    .setName(TAG)
+                    .setName(LAYER_NAME)
                     .build();
         }
 
@@ -194,7 +197,7 @@
             prepareAppIconOverlay(activityInfo);
             mLeash = new SurfaceControl.Builder(new SurfaceSession())
                     .setCallsite(TAG)
-                    .setName(TAG)
+                    .setName(LAYER_NAME)
                     .build();
         }
 
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 f11836e..e9d2571 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
@@ -1594,7 +1594,7 @@
             // source rect hint to enter PiP use bounds animation.
             if (sourceHintRect == null) {
                 if (SystemProperties.getBoolean(
-                        "persist.wm.debug.enable_pip_app_icon_overlay", false)) {
+                        "persist.wm.debug.enable_pip_app_icon_overlay", true)) {
                     animator.setAppIconContentOverlay(
                             mContext, currentBounds, mTaskInfo.topActivityInfo);
                 } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 6b0337d..a91a342 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -804,7 +804,7 @@
                 // We use content overlay when there is no source rect hint to enter PiP use bounds
                 // animation.
                 if (SystemProperties.getBoolean(
-                        "persist.wm.debug.enable_pip_app_icon_overlay", false)) {
+                        "persist.wm.debug.enable_pip_app_icon_overlay", true)) {
                     animator.setAppIconContentOverlay(
                             mContext, currentBounds, taskInfo.topActivityInfo);
                 } else {
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 70d3b35..146abea 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
@@ -764,17 +764,9 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
         if (pendingIntent2 == null) {
-            // Launching a solo task.
-            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
-            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
-            options1 = activityOptions.toBundle();
-            addActivityOptions(options1, null /* launchTarget */);
-            if (shortcutInfo1 != null) {
-                wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
-            } else {
-                wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
-            }
-            mSyncQueue.queue(wct);
+            // Launching a solo intent or shortcut as fullscreen.
+            launchAsFullscreenWithRemoteAnimation(pendingIntent1, fillInIntent1, shortcutInfo1,
+                    options1, adapter, wct);
             return;
         }
 
@@ -797,13 +789,9 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
         if (taskId == INVALID_TASK_ID) {
-            // Launching a solo task.
-            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
-            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
-            options1 = activityOptions.toBundle();
-            addActivityOptions(options1, null /* launchTarget */);
-            wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
-            mSyncQueue.queue(wct);
+            // Launching a solo intent as fullscreen.
+            launchAsFullscreenWithRemoteAnimation(pendingIntent, fillInIntent, null, options1,
+                    adapter, wct);
             return;
         }
 
@@ -822,13 +810,8 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
         if (taskId == INVALID_TASK_ID) {
-            // Launching a solo task.
-            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
-            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
-            options1 = activityOptions.toBundle();
-            addActivityOptions(options1, null /* launchTarget */);
-            wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
-            mSyncQueue.queue(wct);
+            // Launching a solo shortcut as fullscreen.
+            launchAsFullscreenWithRemoteAnimation(null, null, shortcutInfo, options1, adapter, wct);
             return;
         }
 
@@ -838,6 +821,49 @@
                 instanceId);
     }
 
+    private void launchAsFullscreenWithRemoteAnimation(@Nullable PendingIntent pendingIntent,
+            @Nullable Intent fillInIntent, @Nullable ShortcutInfo shortcutInfo,
+            @Nullable Bundle options, RemoteAnimationAdapter adapter,
+            WindowContainerTransaction wct) {
+        LegacyTransitions.ILegacyTransition transition =
+                (transit, apps, wallpapers, nonApps, finishedCallback, t) -> {
+                    if (apps == null || apps.length == 0) {
+                        onRemoteAnimationFinished(apps);
+                        t.apply();
+                        try {
+                            adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "Error starting remote animation", e);
+                        }
+                        return;
+                    }
+
+                    for (int i = 0; i < apps.length; ++i) {
+                        if (apps[i].mode == MODE_OPENING) {
+                            t.show(apps[i].leash);
+                        }
+                    }
+                    t.apply();
+
+                    try {
+                        adapter.getRunner().onAnimationStart(
+                                transit, apps, wallpapers, nonApps, finishedCallback);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Error starting remote animation", e);
+                    }
+                };
+
+        addActivityOptions(options, null /* launchTarget */);
+        if (shortcutInfo != null) {
+            wct.startShortcut(mContext.getPackageName(), shortcutInfo, options);
+        } else if (pendingIntent != null) {
+            wct.sendPendingIntent(pendingIntent, fillInIntent, options);
+        } else {
+            Slog.e(TAG, "Pending intent and shortcut are null is invalid case.");
+        }
+        mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+    }
+
     private void startWithLegacyTransition(WindowContainerTransaction wct,
             @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
             @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle mainOptions,
@@ -894,23 +920,25 @@
 
         if (options == null) options = new Bundle();
         addActivityOptions(options, mMainStage);
-        options = wrapAsSplitRemoteAnimation(adapter, options);
 
         updateWindowBounds(mSplitLayout, wct);
-
-        // TODO(b/268008375): Merge APIs to start a split pair into one.
-        if (mainTaskId != INVALID_TASK_ID) {
-            wct.startTask(mainTaskId, options);
-        } else if (mainShortcutInfo != null) {
-            wct.startShortcut(mContext.getPackageName(), mainShortcutInfo, options);
-        } else {
-            wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, options);
-        }
-
         wct.reorder(mRootTaskInfo.token, true);
         wct.setForceTranslucent(mRootTaskInfo.token, false);
 
-        mSyncQueue.queue(wct);
+        // TODO(b/268008375): Merge APIs to start a split pair into one.
+        if (mainTaskId != INVALID_TASK_ID) {
+            options = wrapAsSplitRemoteAnimation(adapter, options);
+            wct.startTask(mainTaskId, options);
+            mSyncQueue.queue(wct);
+        } else {
+            if (mainShortcutInfo != null) {
+                wct.startShortcut(mContext.getPackageName(), mainShortcutInfo, options);
+            } else {
+                wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, options);
+            }
+            mSyncQueue.queue(wrapAsSplitRemoteAnimation(adapter), WindowManager.TRANSIT_OPEN, wct);
+        }
+
         mSyncQueue.runInSync(t -> {
             setDividerVisibility(true, t);
         });
@@ -967,6 +995,54 @@
         return activityOptions.toBundle();
     }
 
+    private LegacyTransitions.ILegacyTransition wrapAsSplitRemoteAnimation(
+            RemoteAnimationAdapter adapter) {
+        LegacyTransitions.ILegacyTransition transition =
+                (transit, apps, wallpapers, nonApps, finishedCallback, t) -> {
+                    if (apps == null || apps.length == 0) {
+                        onRemoteAnimationFinished(apps);
+                        t.apply();
+                        try {
+                            adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "Error starting remote animation", e);
+                        }
+                        return;
+                    }
+
+                    // Wrap the divider bar into non-apps target to animate together.
+                    nonApps = ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
+                            getDividerBarLegacyTarget());
+
+                    for (int i = 0; i < apps.length; ++i) {
+                        if (apps[i].mode == MODE_OPENING) {
+                            t.show(apps[i].leash);
+                            // Reset the surface position of the opening app to prevent offset.
+                            t.setPosition(apps[i].leash, 0, 0);
+                        }
+                    }
+                    t.apply();
+
+                    IRemoteAnimationFinishedCallback wrapCallback =
+                            new IRemoteAnimationFinishedCallback.Stub() {
+                                @Override
+                                public void onAnimationFinished() throws RemoteException {
+                                    onRemoteAnimationFinished(apps);
+                                    finishedCallback.onAnimationFinished();
+                                }
+                            };
+                    Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
+                    try {
+                        adapter.getRunner().onAnimationStart(
+                                transit, apps, wallpapers, nonApps, wrapCallback);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Error starting remote animation", e);
+                    }
+                };
+
+        return transition;
+    }
+
     private void setEnterInstanceId(InstanceId instanceId) {
         if (instanceId != null) {
             mLogger.enterRequested(instanceId, ENTER_REASON_LAUNCHER);
@@ -993,6 +1069,27 @@
         }
     }
 
+    private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
+        mIsDividerRemoteAnimating = false;
+        mShouldUpdateRecents = true;
+        mSplitRequest = null;
+        // If any stage has no child after finished animation, that side of the split will display
+        // nothing. This might happen if starting the same app on the both sides while not
+        // supporting multi-instance. Exit the split screen and expand that app to full screen.
+        if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+            mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
+                    ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+            mSplitUnsupportedToast.show();
+            return;
+        }
+
+        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+        prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
+        prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
+        mSyncQueue.queue(evictWct);
+    }
+
+
     /**
      * Collects all the current child tasks of a specific split and prepares transaction to evict
      * them to display.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index da80c6f..5c99209 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -80,7 +80,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/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java
index 5ecec4d..3125f08 100644
--- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java
+++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java
@@ -72,6 +72,7 @@
     public static final int AMBIENT_LIGHT_MODE_LOW_LIGHT = 2;
 
     private final DreamManager mDreamManager;
+    private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
 
     @Nullable
     private final ComponentName mLowLightDreamComponent;
@@ -81,8 +82,10 @@
     @Inject
     public LowLightDreamManager(
             DreamManager dreamManager,
+            LowLightTransitionCoordinator lowLightTransitionCoordinator,
             @Named(LOW_LIGHT_DREAM_COMPONENT) @Nullable ComponentName lowLightDreamComponent) {
         mDreamManager = dreamManager;
+        mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
         mLowLightDreamComponent = lowLightDreamComponent;
     }
 
@@ -111,7 +114,9 @@
 
         mAmbientLightMode = ambientLightMode;
 
-        mDreamManager.setSystemDreamComponent(mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT
-                ? mLowLightDreamComponent : null);
+        boolean shouldEnterLowLight = mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT;
+        mLowLightTransitionCoordinator.notifyBeforeLowLightTransition(shouldEnterLowLight,
+                () -> mDreamManager.setSystemDreamComponent(
+                        shouldEnterLowLight ? mLowLightDreamComponent : null));
     }
 }
diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.java b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.java
new file mode 100644
index 0000000..874a2d5
--- /dev/null
+++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dream.lowlight;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Helper class that allows listening and running animations before entering or exiting low light.
+ */
+@Singleton
+public class LowLightTransitionCoordinator {
+    /**
+     * Listener that is notified before low light entry.
+     */
+    public interface LowLightEnterListener {
+        /**
+         * Callback that is notified before the device enters low light.
+         *
+         * @return an optional animator that will be waited upon before entering low light.
+         */
+        Animator onBeforeEnterLowLight();
+    }
+
+    /**
+     * Listener that is notified before low light exit.
+     */
+    public interface LowLightExitListener {
+        /**
+         * Callback that is notified before the device exits low light.
+         *
+         * @return an optional animator that will be waited upon before exiting low light.
+         */
+        Animator onBeforeExitLowLight();
+    }
+
+    private LowLightEnterListener mLowLightEnterListener;
+    private LowLightExitListener mLowLightExitListener;
+
+    @Inject
+    public LowLightTransitionCoordinator() {
+    }
+
+    /**
+     * Sets the listener for the low light enter event.
+     *
+     * Only one listener can be set at a time. This method will overwrite any previously set
+     * listener. Null can be used to unset the listener.
+     */
+    public void setLowLightEnterListener(@Nullable LowLightEnterListener lowLightEnterListener) {
+        mLowLightEnterListener = lowLightEnterListener;
+    }
+
+    /**
+     * Sets the listener for the low light exit event.
+     *
+     * Only one listener can be set at a time. This method will overwrite any previously set
+     * listener. Null can be used to unset the listener.
+     */
+    public void setLowLightExitListener(@Nullable LowLightExitListener lowLightExitListener) {
+        mLowLightExitListener = lowLightExitListener;
+    }
+
+    /**
+     * Notifies listeners that the device is about to enter or exit low light.
+     *
+     * @param entering true if listeners should be notified before entering low light, false if this
+     *                 is notifying before exiting.
+     * @param callback callback that will be run after listeners complete.
+     */
+    void notifyBeforeLowLightTransition(boolean entering, Runnable callback) {
+        Animator animator = null;
+
+        if (entering && mLowLightEnterListener != null) {
+            animator = mLowLightEnterListener.onBeforeEnterLowLight();
+        } else if (!entering && mLowLightExitListener != null) {
+            animator = mLowLightExitListener.onBeforeExitLowLight();
+        }
+
+        // If the listener returned an animator to indicate it was running an animation, run the
+        // callback after the animation completes, otherwise call the callback directly.
+        if (animator != null) {
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animator) {
+                    callback.run();
+                }
+            });
+        } else {
+            callback.run();
+        }
+    }
+}
diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java
index 91a170f..4b95d8c 100644
--- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java
+++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java
@@ -21,7 +21,10 @@
 import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_UNKNOWN;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
@@ -44,44 +47,52 @@
     private DreamManager mDreamManager;
 
     @Mock
+    private LowLightTransitionCoordinator mTransitionCoordinator;
+
+    @Mock
     private ComponentName mDreamComponent;
 
+    LowLightDreamManager mLowLightDreamManager;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+
+        // Automatically run any provided Runnable to mTransitionCoordinator to simplify testing.
+        doAnswer(invocation -> {
+            ((Runnable) invocation.getArgument(1)).run();
+            return null;
+        }).when(mTransitionCoordinator).notifyBeforeLowLightTransition(anyBoolean(),
+                any(Runnable.class));
+
+        mLowLightDreamManager = new LowLightDreamManager(mDreamManager, mTransitionCoordinator,
+                mDreamComponent);
     }
 
     @Test
     public void setAmbientLightMode_lowLight_setSystemDream() {
-        final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
-                mDreamComponent);
+        mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
 
-        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
-
+        verify(mTransitionCoordinator).notifyBeforeLowLightTransition(eq(true), any());
         verify(mDreamManager).setSystemDreamComponent(mDreamComponent);
     }
 
     @Test
     public void setAmbientLightMode_regularLight_clearSystemDream() {
-        final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
-                mDreamComponent);
+        mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
 
-        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
-
+        verify(mTransitionCoordinator).notifyBeforeLowLightTransition(eq(false), any());
         verify(mDreamManager).setSystemDreamComponent(null);
     }
 
     @Test
     public void setAmbientLightMode_defaultUnknownMode_clearSystemDream() {
-        final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
-                mDreamComponent);
-
         // Set to low light first.
-        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+        mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
         clearInvocations(mDreamManager);
 
         // Return to default unknown mode.
-        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);
+        mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);
 
         verify(mDreamManager).setSystemDreamComponent(null);
     }
@@ -89,7 +100,7 @@
     @Test
     public void setAmbientLightMode_dreamComponentNotSet_doNothing() {
         final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
-                null /*dream component*/);
+                mTransitionCoordinator, null /*dream component*/);
 
         lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
 
diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.java b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.java
new file mode 100644
index 0000000..81e1e33
--- /dev/null
+++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.dream.lowlight;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.animation.Animator;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class LowLightTransitionCoordinatorTest {
+    @Mock
+    private LowLightTransitionCoordinator.LowLightEnterListener mEnterListener;
+
+    @Mock
+    private LowLightTransitionCoordinator.LowLightExitListener mExitListener;
+
+    @Mock
+    private Animator mAnimator;
+
+    @Captor
+    private ArgumentCaptor<Animator.AnimatorListener> mAnimatorListenerCaptor;
+
+    @Mock
+    private Runnable mRunnable;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void onEnterCalledOnListeners() {
+        LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
+
+        coordinator.setLowLightEnterListener(mEnterListener);
+
+        coordinator.notifyBeforeLowLightTransition(true, mRunnable);
+
+        verify(mEnterListener).onBeforeEnterLowLight();
+        verify(mRunnable).run();
+    }
+
+    @Test
+    public void onExitCalledOnListeners() {
+        LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
+
+        coordinator.setLowLightExitListener(mExitListener);
+
+        coordinator.notifyBeforeLowLightTransition(false, mRunnable);
+
+        verify(mExitListener).onBeforeExitLowLight();
+        verify(mRunnable).run();
+    }
+
+    @Test
+    public void listenerNotCalledAfterRemoval() {
+        LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
+
+        coordinator.setLowLightEnterListener(mEnterListener);
+        coordinator.setLowLightEnterListener(null);
+
+        coordinator.notifyBeforeLowLightTransition(true, mRunnable);
+
+        verifyZeroInteractions(mEnterListener);
+        verify(mRunnable).run();
+    }
+
+    @Test
+    public void runnableCalledAfterAnimationEnds() {
+        when(mEnterListener.onBeforeEnterLowLight()).thenReturn(mAnimator);
+
+        LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
+        coordinator.setLowLightEnterListener(mEnterListener);
+
+        coordinator.notifyBeforeLowLightTransition(true, mRunnable);
+
+        // Animator listener is added and the runnable is not run yet.
+        verify(mAnimator).addListener(mAnimatorListenerCaptor.capture());
+        verifyZeroInteractions(mRunnable);
+
+        // Runnable is run once the animation ends.
+        mAnimatorListenerCaptor.getValue().onAnimationEnd(null);
+        verify(mRunnable).run();
+    }
+}
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index f17129c..1af60b2 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -108,8 +108,9 @@
         std::move(data), std::string_view(fontPath.c_str(), fontPath.size()),
         fontPtr, fontSize, ttcIndex, builder->axes);
     if (minikinFont == nullptr) {
-        jniThrowException(env, "java/lang/IllegalArgumentException",
-                          "Failed to create internal object. maybe invalid font data.");
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Failed to create internal object. maybe invalid font data. filePath %s",
+                             fontPath.c_str());
         return 0;
     }
     uint32_t localeListId = minikin::registerLocaleList(langTagStr.c_str());
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index f85bdee..5f5e214 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -252,10 +252,10 @@
         if (o == null || getClass() != o.getClass()) return false;
 
         final AudioMix that = (AudioMix) o;
-        return (this.mRouteFlags == that.mRouteFlags)
-                && (this.mRule == that.mRule)
-                && (this.mMixType == that.mMixType)
-                && (this.mFormat == that.mFormat);
+        return (mRouteFlags == that.mRouteFlags)
+                && (mMixType == that.mMixType)
+                && Objects.equals(mRule, that.mRule)
+                && Objects.equals(mFormat, that.mFormat);
     }
 
     /** @hide */
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 440447e..ce97733 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
 import java.util.Objects;
@@ -50,7 +51,8 @@
         mMixes = conf.mMixes;
     }
 
-    AudioPolicyConfig(ArrayList<AudioMix> mixes) {
+    @VisibleForTesting
+    public AudioPolicyConfig(ArrayList<AudioMix> mixes) {
         mMixes = mixes;
     }
 
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index d70e8b3..178a6d97 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -191,20 +191,13 @@
             } else {
                 session = ContentRecordingSession.createTaskSession(launchCookie);
             }
+            // Pass in the current session details, so they are guaranteed to only be set in WMS
+            // AFTER a VirtualDisplay is constructed (assuming there are no errors during set-up).
+            virtualDisplayConfig.setContentRecordingSession(session);
             virtualDisplayConfig.setWindowManagerMirroringEnabled(true);
             final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
             final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this,
                     virtualDisplayConfig.build(), callback, handler, windowContext);
-            if (virtualDisplay == null) {
-                // Since WM handling a new display and DM creating a new VirtualDisplay is async,
-                // WM may have tried to start task recording and encountered an error that required
-                // stopping recording entirely. The VirtualDisplay would then be null when the
-                // MediaProjection is no longer active.
-                return null;
-            }
-            session.setDisplayId(virtualDisplay.getDisplay().getDisplayId());
-            // Successfully set up, so save the current session details.
-            getProjectionService().setContentRecordingSession(session, mImpl);
             return virtualDisplay;
         } catch (RemoteException e) {
             // Can not capture if WMS is not accessible, so bail out.
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index e9aa321..113c858 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -118,6 +118,9 @@
     void requestAd(in IBinder sessionToken, in AdRequest request, int userId);
     void notifyAdBuffer(in IBinder sessionToken, in AdBuffer buffer, int userId);
 
+    // For TV Message
+    void notifyTvMessage(in IBinder sessionToken, in String type, in Bundle data, int userId);
+
     // For TV input hardware binding
     List<TvInputHardwareInfo> getHardwareList();
     ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 82875e5..165a9dd 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -76,4 +76,7 @@
     // For ad request
     void requestAd(in AdRequest request);
     void notifyAdBuffer(in AdBuffer buffer);
+
+    // For TV messages
+    void notifyTvMessage(in String type, in Bundle data);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 465b617..8389706 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -78,6 +78,7 @@
     private static final int DO_SELECT_AUDIO_PRESENTATION = 29;
     private static final int DO_TIME_SHIFT_SET_MODE = 30;
     private static final int DO_SET_TV_MESSAGE_ENABLED = 31;
+    private static final int DO_NOTIFY_TV_MESSAGE = 32;
 
     private final boolean mIsRecordingSession;
     private final HandlerCaller mCaller;
@@ -277,6 +278,11 @@
                 mTvInputSessionImpl.notifyAdBuffer((AdBuffer) msg.obj);
                 break;
             }
+            case DO_NOTIFY_TV_MESSAGE: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mTvInputSessionImpl.onTvMessageReceived((String) args.arg1, (Bundle) args.arg2);
+                break;
+            }
             default: {
                 Log.w(TAG, "Unhandled message code: " + msg.what);
                 break;
@@ -463,6 +469,11 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_BUFFER, buffer));
     }
 
+    @Override
+    public void notifyTvMessage(String type, Bundle data) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_NOTIFY_TV_MESSAGE, type, data));
+    }
+
     private final class TvInputEventReceiver extends InputEventReceiver {
         TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 8459538..55a753f 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -144,6 +144,13 @@
     @StringDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION})
     public @interface TvMessageType {}
 
+    /**
+     * This constant is used as a {@link Bundle} key for TV messages. The value of the key
+     * identifies the stream on the TV input source for which the watermark event is relevant to.
+     */
+    public static final String TV_MESSAGE_KEY_STREAM_ID =
+            "android.media.tv.TvInputManager.stream_id";
+
     static final int VIDEO_UNAVAILABLE_REASON_START = 0;
     static final int VIDEO_UNAVAILABLE_REASON_END = 18;
 
@@ -3221,6 +3228,17 @@
         }
 
         /**
+         * Sends TV messages to the service for testing purposes
+         */
+        public void notifyTvMessage(@NonNull @TvMessageType String type, @NonNull Bundle data) {
+            try {
+                mService.notifyTvMessage(mToken, type, data, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
          * Starts TV program recording in the current recording session.
          *
          * @param programUri The URI for the TV program to record as a hint, built by
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 4bc137d..9f40d70 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1491,7 +1491,18 @@
          *                {@code false} otherwise.
          */
         public void onSetTvMessageEnabled(@NonNull @TvInputManager.TvMessageType String type,
-                boolean enabled){
+                boolean enabled) {
+        }
+
+        /**
+         * Called when a TV message is received
+         *
+         * @param type The type of message received, such as
+         * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
+         * @param data The raw data of the message
+         */
+        public void onTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+                @NonNull Bundle data) {
         }
 
         /**
@@ -2043,6 +2054,10 @@
             onAdBuffer(buffer);
         }
 
+        void onTvMessageReceived(String type, Bundle data) {
+            onTvMessage(type, data);
+        }
+
         /**
          * Takes care of dispatching incoming input events and tells whether the event was handled.
          */
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 3ef61f2..5aeed1f 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.AttributionSource;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -640,6 +641,20 @@
         }
     }
 
+
+    /**
+     * Sends TV messages to the session for testing purposes
+     *
+     * @hide
+     */
+    @TestApi
+    public void notifyTvMessage(@TvInputManager.TvMessageType @NonNull String type,
+            @NonNull Bundle data) {
+        if (mSession != null) {
+            mSession.notifyTvMessage(type, data);
+        }
+    }
+
     /**
      * Sets the callback to be invoked when the time shift position is changed.
      *
diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp
index 63292ce..4624dfe 100644
--- a/media/tests/AudioPolicyTest/Android.bp
+++ b/media/tests/AudioPolicyTest/Android.bp
@@ -14,6 +14,7 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "guava",
+        "guava-android-testlib",
         "hamcrest-library",
         "platform-test-annotations",
     ],
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java
new file mode 100644
index 0000000..bbca882
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java
@@ -0,0 +1,158 @@
+/*
+ * 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.audiopolicytest;
+
+import static android.media.AudioFormat.CHANNEL_OUT_MONO;
+import static android.media.AudioFormat.CHANNEL_OUT_STEREO;
+import static android.media.AudioFormat.ENCODING_PCM_16BIT;
+import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_INJECTOR;
+import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_PLAYERS;
+import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_AUDIO_SESSION_ID;
+import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_UID;
+
+import android.media.AudioFormat;
+import android.media.audiopolicy.AudioMix;
+import android.media.audiopolicy.AudioMixingRule;
+import android.media.audiopolicy.AudioPolicyConfig;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for AudioMix.
+ *
+ * Run with "atest AudioMixUnitTests".
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AudioMixUnitTests {
+    private static final AudioFormat OUTPUT_FORMAT_STEREO_44KHZ_PCM =
+            new AudioFormat.Builder()
+                    .setSampleRate(44000)
+                    .setChannelMask(CHANNEL_OUT_STEREO)
+                    .setEncoding(ENCODING_PCM_16BIT).build();
+    private static final AudioFormat OUTPUT_FORMAT_MONO_16KHZ_PCM =
+            new AudioFormat.Builder()
+                    .setSampleRate(16000)
+                    .setChannelMask(CHANNEL_OUT_MONO)
+                    .setEncoding(ENCODING_PCM_16BIT).build();
+    private static final AudioFormat INPUT_FORMAT_MONO_16KHZ_PCM =
+            new AudioFormat.Builder()
+                    .setSampleRate(16000)
+                    .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                    .setEncoding(ENCODING_PCM_16BIT).build();
+
+    @Test
+    public void testEquals() {
+        final EqualsTester equalsTester = new EqualsTester();
+
+        // --- Equality group 1
+        final AudioMix playbackAudioMixWithSessionId42AndUid123 =
+                new AudioMix.Builder(new AudioMixingRule.Builder()
+                        .setTargetMixRole(MIX_ROLE_PLAYERS)
+                        .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
+                        .addMixRule(RULE_MATCH_UID, 123).build())
+                        .setFormat(OUTPUT_FORMAT_STEREO_44KHZ_PCM)
+                        .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+        final AudioMix playbackAudioMixWithUid123AndSessionId42 =
+                new AudioMix.Builder(new AudioMixingRule.Builder()
+                        .setTargetMixRole(MIX_ROLE_PLAYERS)
+                        .addMixRule(RULE_MATCH_UID, 123)
+                        .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42).build())
+                        .setFormat(OUTPUT_FORMAT_STEREO_44KHZ_PCM)
+                        .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+        equalsTester.addEqualityGroup(
+                playbackAudioMixWithSessionId42AndUid123,
+                playbackAudioMixWithUid123AndSessionId42,
+                writeToAndFromParcel(playbackAudioMixWithSessionId42AndUid123),
+                writeToAndFromParcel(playbackAudioMixWithUid123AndSessionId42));
+
+        // --- Equality group 2
+        final AudioMix recordingAudioMixWithSessionId42AndUid123 =
+                new AudioMix.Builder(new AudioMixingRule.Builder()
+                        .setTargetMixRole(MIX_ROLE_INJECTOR)
+                        .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
+                        .addMixRule(RULE_MATCH_UID, 123).build())
+                        .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
+                        .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+        final AudioMix recordingAudioMixWithUid123AndSessionId42 =
+                new AudioMix.Builder(new AudioMixingRule.Builder()
+                        .setTargetMixRole(MIX_ROLE_INJECTOR)
+                        .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
+                        .addMixRule(RULE_MATCH_UID, 123).build())
+                        .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
+                        .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+        equalsTester.addEqualityGroup(recordingAudioMixWithSessionId42AndUid123,
+                recordingAudioMixWithUid123AndSessionId42,
+                writeToAndFromParcel(recordingAudioMixWithSessionId42AndUid123),
+                writeToAndFromParcel(recordingAudioMixWithUid123AndSessionId42));
+
+        // --- Equality group 3
+        final AudioMix recordingAudioMixWithSessionId42AndUid123Render =
+                new AudioMix.Builder(new AudioMixingRule.Builder()
+                        .setTargetMixRole(MIX_ROLE_INJECTOR)
+                        .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
+                        .addMixRule(RULE_MATCH_UID, 123).build())
+                        .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
+                        .setRouteFlags(
+                                AudioMix.ROUTE_FLAG_LOOP_BACK | AudioMix.ROUTE_FLAG_RENDER).build();
+        equalsTester.addEqualityGroup(recordingAudioMixWithSessionId42AndUid123Render,
+                writeToAndFromParcel(recordingAudioMixWithSessionId42AndUid123Render));
+
+        // --- Equality group 4
+        final AudioMix playbackAudioMixWithUid123 =
+                new AudioMix.Builder(new AudioMixingRule.Builder()
+                        .setTargetMixRole(MIX_ROLE_PLAYERS)
+                        .addMixRule(RULE_MATCH_UID, 123).build())
+                        .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
+                        .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+        equalsTester.addEqualityGroup(playbackAudioMixWithUid123,
+                writeToAndFromParcel(playbackAudioMixWithUid123));
+
+        // --- Equality group 5
+        final AudioMix playbackAudioMixWithUid42 =
+                new AudioMix.Builder(new AudioMixingRule.Builder()
+                        .setTargetMixRole(MIX_ROLE_PLAYERS)
+                        .addMixRule(RULE_MATCH_UID, 42).build())
+                        .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
+                        .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+        equalsTester.addEqualityGroup(playbackAudioMixWithUid42,
+                writeToAndFromParcel(playbackAudioMixWithUid42));
+
+        equalsTester.testEquals();
+    }
+
+    private static AudioMix writeToAndFromParcel(AudioMix audioMix) {
+        AudioPolicyConfig apc = new AudioPolicyConfig(new ArrayList<>(List.of(audioMix)));
+        Parcel parcel = Parcel.obtain();
+        apc.writeToParcel(parcel, /*flags=*/0);
+        parcel.setDataPosition(0);
+        AudioMix unmarshalledMix =
+                AudioPolicyConfig.CREATOR.createFromParcel(parcel).getMixes().get(0);
+        parcel.recycle();
+        return unmarshalledMix;
+    }
+}
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index b842761..82e5a7f 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -36,17 +36,19 @@
     <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile for singleDevice(type) [CHAR LIMIT=NONE] -->
     <string name="summary_watch_single_device">The app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, and access these permissions:</string>
 
-    <!-- TODO(b/256140614) To replace all glasses related strings with final versions -->
     <!-- ================= DEVICE_PROFILE_GLASSES ================= -->
 
+    <!-- Title of the device association confirmation dialog for glasses. -->
+    <string name="confirmation_title_glasses">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to manage &lt;strong&gt;<xliff:g id="device_name" example="Glasses">%2$s</xliff:g>&lt;/strong&gt;?</string>
+
     <!-- The name of the "glasses" device type [CHAR LIMIT=30] -->
     <string name="profile_name_glasses">glasses</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile (type) [CHAR LIMIT=NONE] -->
-    <string name="summary_glasses">This app is needed to manage <xliff:g id="device_name" example="My Glasses">%1$s</xliff:g>. <xliff:g id="app_name" example="Glasses">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string>
+    <string name="summary_glasses_multi_device">This app is needed to manage <xliff:g id="device_name" example="My Glasses">%1$s</xliff:g>. <xliff:g id="app_name" example="Glasses">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile for singleDevice(type) [CHAR LIMIT=NONE] -->
-    <string name="summary_glasses_single_device">The app is needed to manage <xliff:g id="device_name" example="My Glasses">%1$s</xliff:g>. <xliff:g id="app_name" example="Glasses">%2$s</xliff:g> will be allowed to interact with these permissions:</string>
+    <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your phone:</string>
 
     <!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
 
@@ -81,17 +83,13 @@
     <!-- Description of the helper dialog for COMPUTER profile. [CHAR LIMIT=NONE] -->
     <string name="helper_summary_computer"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to access your phone\u2019s photos, media, and notifications</string>
 
-    <!-- TODO(b/256140614) To replace all nearby_device_streaming related strings with final versions -->
     <!-- ================= DEVICE_PROFILE_NEARBY_DEVICE_STREAMING ================= -->
 
     <!-- Confirmation for associating an application with a companion device of NEARBY_DEVICE_STREAMING profile (type) [CHAR LIMIT=NONE] -->
-    <string name="title_nearby_device_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="NearbyStreamer">%1$s</xliff:g>&lt;/strong&gt; to perform this action from your phone</string>
-
-    <!-- Title of the helper dialog for NEARBY_DEVICE_STREAMING profile [CHAR LIMIT=30]. -->
-    <string name="helper_title_nearby_device_streaming">Cross-device services</string>
+    <string name="title_nearby_device_streaming">Allow &lt;strong&gt;<xliff:g id="device_name" example="NearbyStreamer">%1$s</xliff:g>&lt;/strong&gt; to take this action?</string>
 
     <!-- Description of the helper dialog for NEARBY_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] -->
-    <string name="helper_summary_nearby_device_streaming"><xliff:g id="app_name" example="NearbyStreamerApp">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="NearbyDevice">%2$s</xliff:g> to stream content to nearby devices</string>
+    <string name="helper_summary_nearby_device_streaming"><xliff:g id="app_name" example="NearbyStreamerApp">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_name" example="NearbyDevice">%2$s</xliff:g> to stream apps and other system features to nearby devices</string>
 
     <!-- ================= null profile ================= -->
 
@@ -161,7 +159,7 @@
     <string name="permission_app_streaming">Apps</string>
 
     <!-- Nearby_device_streaming permission will be granted to the corresponding profile [CHAR LIMIT=45] -->
-    <string name="permission_nearby_device_streaming">Nearby Device Streaming</string>
+    <string name="permission_nearby_device_streaming">Streaming</string>
 
     <!-- Description of phone permission of corresponding profile [CHAR LIMIT=NONE] -->
     <string name="permission_phone_summary">Can make and manage phone calls</string>
@@ -179,8 +177,7 @@
     <string name="permission_calendar_summary">Can access your calendar</string>
 
     <!-- Description of microphone permission of corresponding profile [CHAR LIMIT=NONE] -->
-    <!-- TODO(b/256140614) Need the description for microphone permission  -->
-    <string name="permission_microphone_summary">Can record audio using the microphone</string>
+    <string name="permission_microphone_summary">Can record audio</string>
 
     <!-- Description of nearby devices' permission of corresponding profile [CHAR LIMIT=NONE] -->
     <string name="permission_nearby_devices_summary">Can find, connect to, and determine the relative position of nearby devices</string>
@@ -195,7 +192,6 @@
     <string name="permission_storage_summary"></string>
 
     <!-- Description of nearby_device_streaming permission of corresponding profile [CHAR LIMIT=NONE] -->
-    <!-- TODO(b/256140614) Need the description for nearby devices' permission  -->
-    <string name="permission_nearby_device_streaming_summary">Stream content to a nearby device</string>
+    <string name="permission_nearby_device_streaming_summary">Stream apps and other system features from your phone</string>
 
 </resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 918f9c6..8316f9d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -30,6 +30,7 @@
 import static com.android.companiondevicemanager.CompanionDeviceResources.MULTI_DEVICES_SUMMARIES;
 import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_TYPES;
 import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILES_NAME;
+import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILES_NAME_MULTI;
 import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICON;
 import static com.android.companiondevicemanager.CompanionDeviceResources.SUMMARIES;
 import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_PROFILES;
@@ -571,6 +572,7 @@
         final String deviceProfile = mRequest.getDeviceProfile();
 
         final String profileName;
+        final String profileNameMulti;
         final Spanned summary;
         final Drawable profileIcon;
         final int summaryResourceId;
@@ -580,6 +582,7 @@
         }
 
         profileName = getString(PROFILES_NAME.get(deviceProfile));
+        profileNameMulti = getString(PROFILES_NAME_MULTI.get(deviceProfile));
         profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile));
         summaryResourceId = MULTI_DEVICES_SUMMARIES.get(deviceProfile);
 
@@ -590,7 +593,7 @@
         }
 
         final Spanned title = getHtmlFromResources(
-                this, R.string.chooser_title, profileName, appLabel);
+                this, R.string.chooser_title, profileNameMulti, appLabel);
 
         mTitle.setText(title);
         mSummary.setText(summary);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
index e3fd354..7aed139 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
@@ -59,7 +59,7 @@
         map.put(DEVICE_PROFILE_COMPUTER, R.string.title_computer);
         map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, R.string.title_nearby_device_streaming);
         map.put(DEVICE_PROFILE_WATCH, R.string.confirmation_title);
-        map.put(DEVICE_PROFILE_GLASSES, R.string.confirmation_title);
+        map.put(DEVICE_PROFILE_GLASSES, R.string.confirmation_title_glasses);
         map.put(null, R.string.confirmation_title);
 
         TITLES = unmodifiableMap(map);
@@ -97,7 +97,7 @@
     static {
         final Map<String, Integer> map = new ArrayMap<>();
         map.put(DEVICE_PROFILE_WATCH, R.string.summary_watch);
-        map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses);
+        map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses_multi_device);
         map.put(null, R.string.summary_generic);
 
         MULTI_DEVICES_SUMMARIES = unmodifiableMap(map);
@@ -113,6 +113,16 @@
         PROFILES_NAME = unmodifiableMap(map);
     }
 
+    static final Map<String, Integer> PROFILES_NAME_MULTI;
+    static {
+        final Map<String, Integer> map = new ArrayMap<>();
+        map.put(DEVICE_PROFILE_GLASSES, R.string.profile_name_generic);
+        map.put(DEVICE_PROFILE_WATCH, R.string.profile_name_watch);
+        map.put(null, R.string.profile_name_generic);
+
+        PROFILES_NAME_MULTI = unmodifiableMap(map);
+    }
+
     static final Map<String, Integer> PROFILE_ICON;
     static {
         final Map<String, Integer> map = new ArrayMap<>();
@@ -133,7 +143,6 @@
         SUPPORTED_PROFILES = unmodifiableSet(set);
     }
 
-
     static final Set<String> SUPPORTED_SELF_MANAGED_PROFILES;
     static {
         final Set<String> set = new ArraySet<>();
@@ -145,6 +154,4 @@
 
         SUPPORTED_SELF_MANAGED_PROFILES = unmodifiableSet(set);
     }
-
-
 }
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
index eae14a6..8f32dbb 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
@@ -21,6 +21,7 @@
 import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING;
 
 import static com.android.companiondevicemanager.Utils.getApplicationIcon;
+import static com.android.companiondevicemanager.Utils.getApplicationLabel;
 import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
 
 import android.annotation.Nullable;
@@ -105,9 +106,11 @@
         final String packageName = request.getPackageName();
         final CharSequence displayName = request.getDisplayName();
         final int userId = request.getUserId();
+        final CharSequence appLabel;
 
         try {
             applicationIcon = getApplicationIcon(getContext(), packageName);
+            appLabel = getApplicationLabel(getContext(), packageName, userId);
         } catch (PackageManager.NameNotFoundException e) {
             Log.e(TAG, "Package u" + userId + "/" + packageName + " not found.");
             mListener.onShowHelperDialogFailed();
@@ -119,7 +122,7 @@
         mAppIcon = view.findViewById(R.id.app_icon);
         mButton = view.findViewById(R.id.btn_back);
 
-        final Spanned title;
+        final CharSequence title;
         final Spanned summary;
 
         switch (deviceProfile) {
@@ -136,8 +139,7 @@
                 break;
 
             case DEVICE_PROFILE_NEARBY_DEVICE_STREAMING:
-                title = getHtmlFromResources(getContext(),
-                        R.string.helper_title_nearby_device_streaming);
+                title = appLabel;
                 summary = getHtmlFromResources(
                         getContext(), R.string.helper_summary_nearby_device_streaming, title,
                         displayName);
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index ca30c53..e7de8b3 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -22,6 +22,7 @@
 import androidx.activity.compose.ManagedActivityResultLauncher
 import androidx.activity.result.ActivityResult
 import androidx.activity.result.IntentSenderRequest
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -36,6 +37,8 @@
 import com.android.credentialmanager.createflow.CreateScreenState
 import com.android.credentialmanager.getflow.GetCredentialUiState
 import com.android.credentialmanager.getflow.GetScreenState
+import com.android.credentialmanager.logging.UIMetrics
+import com.android.internal.logging.UiEventLogger.UiEventEnum
 
 /** One and only one of create or get state can be active at any given time. */
 data class UiState(
@@ -44,7 +47,7 @@
     val selectedEntry: BaseEntry? = null,
     val providerActivityState: ProviderActivityState = ProviderActivityState.NOT_APPLICABLE,
     val dialogState: DialogState = DialogState.ACTIVE,
-    // True if the UI has one and onely one auto selectable entry. Its provider activiey will be
+    // True if the UI has one and only one auto selectable entry. Its provider activity will be
     // launched immediately, and canceling it will cancel the whole UI flow.
     val isAutoSelectFlow: Boolean = false,
 )
@@ -56,6 +59,8 @@
     var uiState by mutableStateOf(credManRepo.initState())
         private set
 
+    var uiMetrics: UIMetrics = UIMetrics()
+
     /**************************************************************************/
     /*****                       Shared Callbacks                         *****/
     /**************************************************************************/
@@ -76,6 +81,10 @@
     fun onNewCredentialManagerRepo(credManRepo: CredentialManagerRepo) {
         this.credManRepo = credManRepo
         uiState = credManRepo.initState()
+
+        if (this.credManRepo.requestInfo.token != credManRepo.requestInfo.token) {
+            this.uiMetrics.resetInstanceId()
+        }
     }
 
     fun launchProviderUi(
@@ -374,4 +383,9 @@
             onInternalError()
         }
     }
+
+    @Composable
+    fun logUiEvent(uiEventEnum: UiEventEnum) {
+        this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo.appPackageName)
+    }
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index e61633f..b5c8989 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -60,8 +60,7 @@
 import androidx.credentials.provider.CustomCredentialEntry
 import androidx.credentials.provider.PasswordCredentialEntry
 import androidx.credentials.provider.PublicKeyCredentialEntry
-import androidx.credentials.provider.RemoteCreateEntry
-import androidx.credentials.provider.RemoteCredentialEntry
+import androidx.credentials.provider.RemoteEntry
 import org.json.JSONObject
 
 // TODO: remove all !! checks
@@ -336,7 +335,7 @@
             if (remoteEntry == null) {
                 return null
             }
-            val structuredRemoteEntry = RemoteCredentialEntry.fromSlice(remoteEntry.slice)
+            val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
                 ?: return null
             return RemoteEntryInfo(
                 providerId = providerId,
@@ -628,7 +627,7 @@
             remoteEntry: Entry?,
         ): RemoteInfo? {
             return if (remoteEntry != null) {
-                val structuredRemoteEntry = RemoteCreateEntry.fromSlice(remoteEntry.slice)
+                val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
                     ?: return null
                 RemoteInfo(
                     providerId = providerId,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
index 82d6952..26aadd9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
@@ -32,8 +32,7 @@
 import androidx.credentials.provider.CreateEntry
 import androidx.credentials.provider.PasswordCredentialEntry
 import androidx.credentials.provider.PublicKeyCredentialEntry
-import androidx.credentials.provider.RemoteCreateEntry
-import androidx.credentials.provider.RemoteCredentialEntry
+import androidx.credentials.provider.RemoteEntry
 
 import java.time.Instant
 
@@ -85,9 +84,7 @@
             return Entry(
                 key,
                 subkey,
-                RemoteCredentialEntry(pendingIntent, BeginGetPublicKeyCredentialOption(
-                    Bundle(), "id", "requestjson"
-                )).slice
+                RemoteEntry(pendingIntent).slice
             )
         }
 
@@ -244,7 +241,7 @@
             return Entry(
                 key,
                 subkey,
-                RemoteCreateEntry(pendingIntent).slice
+                RemoteEntry(pendingIntent).slice
             )
         }
     }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index c0c29bb..1923542 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -102,8 +102,9 @@
                     SmallTitleText(text = entryHeadlineText, enforceOneLine = enforceOneLine)
                     if (passwordValue != null) {
                         Row(
-                            modifier = Modifier.fillMaxWidth(),
+                            modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
                             verticalAlignment = Alignment.CenterVertically,
+                            horizontalArrangement = Arrangement.Start,
                         ) {
                             val visualTransformation = remember { PasswordVisualTransformation() }
                             val originalPassword by remember {
@@ -117,9 +118,13 @@
                                 )
                             }
                             BodySmallText(
-                                text = displayedPassword.value, enforceOneLine = enforceOneLine)
+                                text = displayedPassword.value,
+                                // Apply weight to allow visibility button to render first so that
+                                // it doesn't get squeezed out by a super long password.
+                                modifier = Modifier.wrapContentSize().weight(1f, fill = false),
+                            )
                             ToggleVisibilityButton(
-                                modifier = Modifier.padding(start = 12.dp, top = 5.dp).size(24.dp),
+                                modifier = Modifier.padding(start = 12.dp).size(24.dp),
                                 onToggle = {
                                     if (it) {
                                         displayedPassword.value = originalPassword
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 9fe7899..5240777 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -67,6 +67,8 @@
 import com.android.credentialmanager.common.ui.PasskeyBenefitRow
 import com.android.credentialmanager.common.ui.HeadlineText
 import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor
+import com.android.credentialmanager.logging.CreateCredentialEvent
+import com.android.internal.logging.UiEventLogger.UiEventEnum
 
 @Composable
 fun CreateCredentialScreen(
@@ -85,81 +87,93 @@
                 ProviderActivityState.NOT_APPLICABLE -> {
                     when (createCredentialUiState.currentScreenState) {
                         CreateScreenState.PASSKEY_INTRO -> PasskeyIntroCard(
-                            onConfirm = viewModel::createFlowOnConfirmIntro,
-                            onLearnMore = viewModel::createFlowOnLearnMore,
+                                onConfirm = viewModel::createFlowOnConfirmIntro,
+                                onLearnMore = viewModel::createFlowOnLearnMore,
+                                onLog = { viewModel.logUiEvent(it) },
                         )
                         CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
-                            requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
-                            disabledProviderList = createCredentialUiState.disabledProviders,
-                            sortedCreateOptionsPairs =
-                            createCredentialUiState.sortedCreateOptionsPairs,
-                            hasRemoteEntry = createCredentialUiState.remoteEntry != null,
-                            onOptionSelected =
-                            viewModel::createFlowOnEntrySelectedFromFirstUseScreen,
-                            onDisabledProvidersSelected =
-                            viewModel::createFlowOnDisabledProvidersSelected,
-                            onMoreOptionsSelected =
-                            viewModel::createFlowOnMoreOptionsSelectedOnProviderSelection,
+                                requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
+                                disabledProviderList = createCredentialUiState
+                                        .disabledProviders,
+                                sortedCreateOptionsPairs =
+                                createCredentialUiState.sortedCreateOptionsPairs,
+                                hasRemoteEntry = createCredentialUiState.remoteEntry != null,
+                                onOptionSelected =
+                                viewModel::createFlowOnEntrySelectedFromFirstUseScreen,
+                                onDisabledProvidersSelected =
+                                viewModel::createFlowOnDisabledProvidersSelected,
+                                onMoreOptionsSelected =
+                                viewModel::createFlowOnMoreOptionsSelectedOnProviderSelection,
+                                onLog = { viewModel.logUiEvent(it) },
                         )
                         CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
-                            requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
-                            enabledProviderList = createCredentialUiState.enabledProviders,
-                            providerInfo = createCredentialUiState.activeEntry?.activeProvider!!,
-                            hasDefaultProvider = createCredentialUiState.hasDefaultProvider,
-                            createOptionInfo =
-                            createCredentialUiState.activeEntry.activeEntryInfo
-                                as CreateOptionInfo,
-                            onOptionSelected = viewModel::createFlowOnEntrySelected,
-                            onConfirm = viewModel::createFlowOnConfirmEntrySelected,
-                            onMoreOptionsSelected =
-                            viewModel::createFlowOnMoreOptionsSelectedOnCreationSelection,
+                                requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
+                                enabledProviderList = createCredentialUiState.enabledProviders,
+                                providerInfo = createCredentialUiState
+                                        .activeEntry?.activeProvider!!,
+                                hasDefaultProvider = createCredentialUiState.hasDefaultProvider,
+                                createOptionInfo =
+                                createCredentialUiState.activeEntry.activeEntryInfo
+                                        as CreateOptionInfo,
+                                onOptionSelected = viewModel::createFlowOnEntrySelected,
+                                onConfirm = viewModel::createFlowOnConfirmEntrySelected,
+                                onMoreOptionsSelected =
+                                viewModel::createFlowOnMoreOptionsSelectedOnCreationSelection,
+                                onLog = { viewModel.logUiEvent(it) },
                         )
                         CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard(
-                            requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
-                            enabledProviderList = createCredentialUiState.enabledProviders,
-                            disabledProviderList = createCredentialUiState.disabledProviders,
-                            sortedCreateOptionsPairs =
-                            createCredentialUiState.sortedCreateOptionsPairs,
-                            hasDefaultProvider = createCredentialUiState.hasDefaultProvider,
-                            isFromProviderSelection =
-                            createCredentialUiState.isFromProviderSelection!!,
-                            onBackProviderSelectionButtonSelected =
-                            viewModel::createFlowOnBackProviderSelectionButtonSelected,
-                            onBackCreationSelectionButtonSelected =
-                            viewModel::createFlowOnBackCreationSelectionButtonSelected,
-                            onOptionSelected =
-                            viewModel::createFlowOnEntrySelectedFromMoreOptionScreen,
-                            onDisabledProvidersSelected =
-                            viewModel::createFlowOnDisabledProvidersSelected,
-                            onRemoteEntrySelected = viewModel::createFlowOnEntrySelected,
+                                requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
+                                enabledProviderList = createCredentialUiState.enabledProviders,
+                                disabledProviderList = createCredentialUiState
+                                        .disabledProviders,
+                                sortedCreateOptionsPairs =
+                                createCredentialUiState.sortedCreateOptionsPairs,
+                                hasDefaultProvider = createCredentialUiState.hasDefaultProvider,
+                                isFromProviderSelection =
+                                createCredentialUiState.isFromProviderSelection!!,
+                                onBackProviderSelectionButtonSelected =
+                                viewModel::createFlowOnBackProviderSelectionButtonSelected,
+                                onBackCreationSelectionButtonSelected =
+                                viewModel::createFlowOnBackCreationSelectionButtonSelected,
+                                onOptionSelected =
+                                viewModel::createFlowOnEntrySelectedFromMoreOptionScreen,
+                                onDisabledProvidersSelected =
+                                viewModel::createFlowOnDisabledProvidersSelected,
+                                onRemoteEntrySelected = viewModel::createFlowOnEntrySelected,
+                                onLog = { viewModel.logUiEvent(it) },
                         )
                         CreateScreenState.MORE_OPTIONS_ROW_INTRO -> {
                             if (createCredentialUiState.activeEntry == null) {
                                 viewModel.onIllegalUiState("Expect active entry to be non-null" +
-                                    " upon default provider dialog.")
+                                        " upon default provider dialog.")
                             } else {
                                 MoreOptionsRowIntroCard(
-                                    selectedEntry = createCredentialUiState.activeEntry,
-                                    onIllegalScreenState = viewModel::onIllegalUiState,
-                                    onChangeDefaultSelected =
-                                    viewModel::createFlowOnChangeDefaultSelected,
-                                    onUseOnceSelected = viewModel::createFlowOnUseOnceSelected,
+                                        selectedEntry = createCredentialUiState.activeEntry,
+                                        onIllegalScreenState = viewModel::onIllegalUiState,
+                                        onChangeDefaultSelected =
+                                        viewModel::createFlowOnChangeDefaultSelected,
+                                        onUseOnceSelected = viewModel::createFlowOnUseOnceSelected,
+                                        onLog = { viewModel.logUiEvent(it) },
                                 )
                             }
                         }
                         CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard(
-                            requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
-                            activeRemoteEntry =
-                            createCredentialUiState.activeEntry?.activeEntryInfo!!,
-                            onOptionSelected = viewModel::createFlowOnEntrySelected,
-                            onConfirm = viewModel::createFlowOnConfirmEntrySelected,
+                                requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
+                                activeRemoteEntry =
+                                createCredentialUiState.activeEntry?.activeEntryInfo!!,
+                                onOptionSelected = viewModel::createFlowOnEntrySelected,
+                                onConfirm = viewModel::createFlowOnConfirmEntrySelected,
+                                onLog = { viewModel.logUiEvent(it) },
                         )
-                        CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO ->
-                            MoreAboutPasskeysIntroCard(
+                        CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO -> MoreAboutPasskeysIntroCard(
                                 onBackPasskeyIntroButtonSelected =
                                 viewModel::createFlowOnBackPasskeyIntroButtonSelected,
-                            )
+                                onLog = { viewModel.logUiEvent(it) },
+                        )
                     }
+                    viewModel.uiMetrics.log(
+                            CreateCredentialEvent
+                                    .CREDMAN_CREATE_CRED_PROVIDER_ACTIVITY_NOT_APPLICABLE)
                 }
                 ProviderActivityState.READY_TO_LAUNCH -> {
                     // Launch only once per providerActivityState change so that the provider
@@ -167,9 +181,14 @@
                     LaunchedEffect(viewModel.uiState.providerActivityState) {
                         viewModel.launchProviderUi(providerActivityLauncher)
                     }
+                    viewModel.uiMetrics.log(
+                            CreateCredentialEvent
+                                    .CREDMAN_CREATE_CRED_PROVIDER_ACTIVITY_READY_TO_LAUNCH)
                 }
                 ProviderActivityState.PENDING -> {
                     // Hide our content when the provider activity is active.
+                    viewModel.uiMetrics.log(
+                            CreateCredentialEvent.CREDMAN_CREATE_CRED_PROVIDER_ACTIVITY_PENDING)
                 }
             }
         },
@@ -181,6 +200,7 @@
 fun PasskeyIntroCard(
     onConfirm: () -> Unit,
     onLearnMore: () -> Unit,
+    onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard {
         item {
@@ -245,6 +265,7 @@
             )
         }
     }
+    onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_PASSKEY_INTRO)
 }
 
 @Composable
@@ -256,6 +277,7 @@
     onOptionSelected: (ActiveEntry) -> Unit,
     onDisabledProvidersSelected: () -> Unit,
     onMoreOptionsSelected: () -> Unit,
+    onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard {
         item { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) }
@@ -319,21 +341,23 @@
             }
         }
     }
+    onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_PROVIDER_SELECTION)
 }
 
 @Composable
 fun MoreOptionsSelectionCard(
-    requestDisplayInfo: RequestDisplayInfo,
-    enabledProviderList: List<EnabledProviderInfo>,
-    disabledProviderList: List<DisabledProviderInfo>?,
-    sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
-    hasDefaultProvider: Boolean,
-    isFromProviderSelection: Boolean,
-    onBackProviderSelectionButtonSelected: () -> Unit,
-    onBackCreationSelectionButtonSelected: () -> Unit,
-    onOptionSelected: (ActiveEntry) -> Unit,
-    onDisabledProvidersSelected: () -> Unit,
-    onRemoteEntrySelected: (BaseEntry) -> Unit,
+        requestDisplayInfo: RequestDisplayInfo,
+        enabledProviderList: List<EnabledProviderInfo>,
+        disabledProviderList: List<DisabledProviderInfo>?,
+        sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+        hasDefaultProvider: Boolean,
+        isFromProviderSelection: Boolean,
+        onBackProviderSelectionButtonSelected: () -> Unit,
+        onBackCreationSelectionButtonSelected: () -> Unit,
+        onOptionSelected: (ActiveEntry) -> Unit,
+        onDisabledProvidersSelected: () -> Unit,
+        onRemoteEntrySelected: (BaseEntry) -> Unit,
+        onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard(topAppBar = {
         MoreOptionTopAppBar(
@@ -394,14 +418,16 @@
             }
         }
     }
+    onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_MORE_OPTIONS_SELECTION)
 }
 
 @Composable
 fun MoreOptionsRowIntroCard(
-    selectedEntry: ActiveEntry,
-    onIllegalScreenState: (String) -> Unit,
-    onChangeDefaultSelected: () -> Unit,
-    onUseOnceSelected: () -> Unit,
+        selectedEntry: ActiveEntry,
+        onIllegalScreenState: (String) -> Unit,
+        onChangeDefaultSelected: () -> Unit,
+        onUseOnceSelected: () -> Unit,
+        onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     val entryInfo = selectedEntry.activeEntryInfo
     if (entryInfo !is CreateOptionInfo) {
@@ -440,18 +466,20 @@
             )
         }
     }
+    onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_MORE_OPTIONS_ROW_INTRO)
 }
 
 @Composable
 fun CreationSelectionCard(
-    requestDisplayInfo: RequestDisplayInfo,
-    enabledProviderList: List<EnabledProviderInfo>,
-    providerInfo: EnabledProviderInfo,
-    createOptionInfo: CreateOptionInfo,
-    onOptionSelected: (BaseEntry) -> Unit,
-    onConfirm: () -> Unit,
-    onMoreOptionsSelected: () -> Unit,
-    hasDefaultProvider: Boolean,
+        requestDisplayInfo: RequestDisplayInfo,
+        enabledProviderList: List<EnabledProviderInfo>,
+        providerInfo: EnabledProviderInfo,
+        createOptionInfo: CreateOptionInfo,
+        onOptionSelected: (BaseEntry) -> Unit,
+        onConfirm: () -> Unit,
+        onMoreOptionsSelected: () -> Unit,
+        hasDefaultProvider: Boolean,
+        onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard {
         item {
@@ -537,14 +565,16 @@
             item { BodySmallText(text = createOptionInfo.footerDescription) }
         }
     }
+    onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_CREATION_OPTION_SELECTION)
 }
 
 @Composable
 fun ExternalOnlySelectionCard(
-    requestDisplayInfo: RequestDisplayInfo,
-    activeRemoteEntry: BaseEntry,
-    onOptionSelected: (BaseEntry) -> Unit,
-    onConfirm: () -> Unit,
+        requestDisplayInfo: RequestDisplayInfo,
+        activeRemoteEntry: BaseEntry,
+        onOptionSelected: (BaseEntry) -> Unit,
+        onConfirm: () -> Unit,
+        onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard {
         item { HeadlineIcon(imageVector = Icons.Outlined.QrCodeScanner) }
@@ -572,11 +602,13 @@
             )
         }
     }
+    onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_EXTERNAL_ONLY_SELECTION)
 }
 
 @Composable
 fun MoreAboutPasskeysIntroCard(
     onBackPasskeyIntroButtonSelected: () -> Unit,
+    onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard(
         topAppBar = {
@@ -612,6 +644,7 @@
             BodyMediumText(text = stringResource(R.string.seamless_transition_detail))
         }
     }
+    onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO)
 }
 
 @Composable
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index ab947ae..a9f994d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -295,13 +295,6 @@
                 )
             }
         }
-        item {
-            Divider(
-                thickness = 1.dp,
-                color = Color.LightGray,
-                modifier = Modifier.padding(top = 16.dp)
-            )
-        }
         // Manage sign-ins (action chips)
         item {
             ActionChips(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
new file mode 100644
index 0000000..daa42be
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.credentialmanager.logging
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class CreateCredentialEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+
+    @UiEvent(doc = "The create credential bottomsheet became visible on the screen.")
+    CREDMAN_CREATE_CRED_BOTTOMSHEET(1318),
+
+    @UiEvent(doc = "The provider activity is launched on the screen.")
+    CREDMAN_CREATE_CRED_PROVIDER_ACTIVITY_READY_TO_LAUNCH(1319),
+
+    @UiEvent(doc = "The provider activity is launched and we are waiting for its result. " +
+            "Contents Hidden.")
+    CREDMAN_CREATE_CRED_PROVIDER_ACTIVITY_PENDING(1320),
+
+    @UiEvent(doc = "The provider activity is not active or ready launched on the screen.")
+    CREDMAN_CREATE_CRED_PROVIDER_ACTIVITY_NOT_APPLICABLE(1321),
+
+    @UiEvent(doc = "The passkey introduction card is visible on screen.")
+    CREDMAN_CREATE_CRED_PASSKEY_INTRO(1322),
+
+    @UiEvent(doc = "The provider selection card is visible on screen.")
+    CREDMAN_CREATE_CRED_PROVIDER_SELECTION(1323),
+
+    @UiEvent(doc = "The creation option selection card is visible on screen.")
+    CREDMAN_CREATE_CRED_CREATION_OPTION_SELECTION(1324),
+
+    @UiEvent(doc = "The more option selection card is visible on screen.")
+    CREDMAN_CREATE_CRED_MORE_OPTIONS_SELECTION(1325),
+
+    @UiEvent(doc = "The more options row intro card is visible on screen.")
+    CREDMAN_CREATE_CRED_MORE_OPTIONS_ROW_INTRO(1326),
+
+    @UiEvent(doc = "The external only selection card is visible on screen.")
+    CREDMAN_CREATE_CRED_EXTERNAL_ONLY_SELECTION(1327),
+
+    @UiEvent(doc = "The more about passkeys intro card is visible on screen.")
+    CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO(1328);
+
+    override fun getId(): Int {
+        return this.id
+    }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/logging/UIMetrics.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/UIMetrics.kt
new file mode 100644
index 0000000..4351e84
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/UIMetrics.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.credentialmanager.logging
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.UiEventLoggerImpl
+
+class UIMetrics() {
+    private val INSTANCE_ID_MAX = 1 shl 20
+    private val mUiEventLogger: UiEventLogger = UiEventLoggerImpl()
+    val mInstanceIdSequence: InstanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX)
+
+    var mInstanceId: InstanceId = mInstanceIdSequence.newInstanceId()
+
+    fun resetInstanceId() {
+        this.mInstanceId = mInstanceIdSequence.newInstanceId()
+    }
+
+    @Composable
+    fun log(event: UiEventLogger.UiEventEnum) {
+        val instanceId: InstanceId = mInstanceId
+        LaunchedEffect(true) {
+            mUiEventLogger.log(event, instanceId)
+        }
+    }
+
+    @Composable
+    fun log(event: UiEventLogger.UiEventEnum, packageName: String) {
+        val instanceId: InstanceId = mInstanceId
+        LaunchedEffect(true) {
+            mUiEventLogger.logWithInstanceId(event, /*uid=*/0, packageName, instanceId)
+        }
+    }
+
+    @Composable
+    fun log(event: UiEventLogger.UiEventEnum, instanceId: InstanceId, packageName: String) {
+        LaunchedEffect(true) {
+            mUiEventLogger.logWithInstanceId(event, /*uid=*/0, packageName, instanceId)
+        }
+    }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 4ed7e19..10b004e 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -29,6 +29,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.SparseIntArray;
 
@@ -36,6 +37,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -57,6 +59,7 @@
     private final SecureSettings mSecureSettings;
     private String[] mDeviceStateRotationLockDefaults;
     private SparseIntArray mDeviceStateRotationLockSettings;
+    private SparseIntArray mDeviceStateDefaultRotationLockSettings;
     private SparseIntArray mDeviceStateRotationLockFallbackSettings;
     private String mLastSettingValue;
     private List<SettableDeviceState> mSettableDeviceStates;
@@ -93,9 +96,7 @@
     /** Returns true if device-state based rotation lock settings are enabled. */
     public static boolean isDeviceStateRotationLockEnabled(Context context) {
         return context.getResources()
-                        .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
-                        .length
-                > 0;
+                .getStringArray(R.array.config_perDeviceStateRotationLockDefaults).length > 0;
     }
 
     private void listenForSettingsChange() {
@@ -228,6 +229,15 @@
             try {
                 key = Integer.parseInt(values[i++]);
                 value = Integer.parseInt(values[i++]);
+                boolean isPersistedValueIgnored = value == DEVICE_STATE_ROTATION_LOCK_IGNORED;
+                boolean isDefaultValueIgnored = mDeviceStateDefaultRotationLockSettings.get(key)
+                        == DEVICE_STATE_ROTATION_LOCK_IGNORED;
+                if (isPersistedValueIgnored != isDefaultValueIgnored) {
+                    Log.w(TAG, "Conflict for ignored device state " + key
+                            + ". Falling back on defaults");
+                    fallbackOnDefaults();
+                    return;
+                }
                 mDeviceStateRotationLockSettings.put(key, value);
             } catch (NumberFormatException e) {
                 Log.wtf(TAG, "Error deserializing one of the saved settings", e);
@@ -276,6 +286,9 @@
     }
 
     private void persistSettingIfChanged(String newSettingValue) {
+        Log.v(TAG, "persistSettingIfChanged: "
+                + "last=" + mLastSettingValue + ", "
+                + "new=" + newSettingValue);
         if (TextUtils.equals(mLastSettingValue, newSettingValue)) {
             return;
         }
@@ -288,6 +301,8 @@
 
     private void loadDefaults() {
         mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length);
+        mDeviceStateDefaultRotationLockSettings = new SparseIntArray(
+                mDeviceStateRotationLockDefaults.length);
         mDeviceStateRotationLockSettings = new SparseIntArray(
                 mDeviceStateRotationLockDefaults.length);
         mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
@@ -311,6 +326,7 @@
                 boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED;
                 mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable));
                 mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
+                mDeviceStateDefaultRotationLockSettings.put(deviceState, rotationLockSetting);
             } catch (NumberFormatException e) {
                 Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
                 return;
@@ -318,6 +334,22 @@
         }
     }
 
+    /** Dumps internal state. */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("DeviceStateRotationLockSettingsManager");
+        pw.increaseIndent();
+        pw.println("mDeviceStateRotationLockDefaults: " + Arrays.toString(
+                mDeviceStateRotationLockDefaults));
+        pw.println("mDeviceStateDefaultRotationLockSettings: "
+                + mDeviceStateDefaultRotationLockSettings);
+        pw.println("mDeviceStateRotationLockSettings: " + mDeviceStateRotationLockSettings);
+        pw.println("mDeviceStateRotationLockFallbackSettings: "
+                + mDeviceStateRotationLockFallbackSettings);
+        pw.println("mSettableDeviceStates: " + mSettableDeviceStates);
+        pw.println("mLastSettingValue: " + mLastSettingValue);
+        pw.decreaseIndent();
+    }
+
     /**
      * Called when the persisted settings have changed, requiring a reinitialization of the
      * in-memory map.
@@ -372,5 +404,13 @@
         public int hashCode() {
             return Objects.hash(mDeviceState, mIsSettable);
         }
+
+        @Override
+        public String toString() {
+            return "SettableDeviceState{"
+                    + "mDeviceState=" + mDeviceState
+                    + ", mIsSettable=" + mIsSettable
+                    + '}';
+        }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
index 78df0f2..ca88f8d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.spa.framework.common
 
+import android.app.settings.SettingsEnums
 import android.os.Bundle
 
 // Defines the category of the log, for quick filter
@@ -31,20 +32,21 @@
 }
 
 // Defines the log events in Spa.
-enum class LogEvent {
+enum class LogEvent(val action: Int) {
     // Page related events.
-    PAGE_ENTER,
-    PAGE_LEAVE,
+    PAGE_ENTER(SettingsEnums.PAGE_VISIBLE),
+    PAGE_LEAVE(SettingsEnums.PAGE_HIDE),
 
     // Entry related events.
-    ENTRY_CLICK,
-    ENTRY_SWITCH,
+    ENTRY_CLICK(SettingsEnums.ACTION_SETTINGS_TILE_CLICK),
+    ENTRY_SWITCH(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
 }
 
 internal const val LOG_DATA_DISPLAY_NAME = "name"
-internal const val LOG_DATA_SESSION_NAME = "session"
 internal const val LOG_DATA_SWITCH_STATUS = "switch"
 
+const val LOG_DATA_SESSION_NAME = "session"
+
 /**
  * The interface of logger in Spa
  */
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SpaIntent.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SpaIntent.kt
index 2c3c2e0..d8c35a3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SpaIntent.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SpaIntent.kt
@@ -22,9 +22,11 @@
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 
+const val SESSION_UNKNOWN = "unknown"
 const val SESSION_BROWSE = "browse"
 const val SESSION_SEARCH = "search"
 const val SESSION_SLICE = "slice"
+const val SESSION_EXTERNAL = "external"
 
 const val KEY_DESTINATION = "spaActivityDestination"
 const val KEY_HIGHLIGHT_ENTRY = "highlightEntry"
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b92b3d6..8e7d36e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1648,4 +1648,7 @@
         <item>Move right</item>
         <item>Move up</item>
     </string-array>
+
+    <!-- Formatting states for the scale of font size, in percent. Double "%" is required to represent the symbol "%". [CHAR LIMIT=20] -->
+    <string name="font_scale_percentage"> <xliff:g id="percentage">%1$d</xliff:g> %%</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index fb06976..3ec5eba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -23,6 +23,7 @@
 
 import androidx.annotation.Keep;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -132,6 +133,11 @@
         }
     }
 
+    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    public boolean isSwitchEnabled() {
+        return mEnableSwitch;
+    }
+
     /**
      * If admin is not null, disables the switch.
      * Otherwise, keep it enabled.
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 81006dd..0fa15eb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -33,7 +33,10 @@
 import com.android.internal.R;
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -45,6 +48,8 @@
 @RunWith(AndroidJUnit4.class)
 public class DeviceStateRotationLockSettingsManagerTest {
 
+    @Rule public Expect mExpect = Expect.create();
+
     @Mock private Context mMockContext;
     @Mock private Resources mMockResources;
 
@@ -117,4 +122,40 @@
                 new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false)
         ).inOrder();
     }
+
+    @Test
+    public void persistedInvalidIgnoredState_returnsDefaults() {
+        when(mMockResources.getStringArray(
+                R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+                new String[]{"0:1", "1:0:2", "2:2"});
+        // Here 2 has IGNORED, and in the defaults 1 has IGNORED.
+        persistSettings("0:2:2:0:1:2");
+        DeviceStateRotationLockSettingsManager manager =
+                new DeviceStateRotationLockSettingsManager(mMockContext, mFakeSecureSettings);
+
+        mExpect.that(manager.getRotationLockSetting(0)).isEqualTo(1);
+        mExpect.that(manager.getRotationLockSetting(1)).isEqualTo(2);
+        mExpect.that(manager.getRotationLockSetting(2)).isEqualTo(2);
+    }
+
+    @Test
+    public void persistedValidValues_returnsPersistedValues() {
+        when(mMockResources.getStringArray(
+                R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+                new String[]{"0:1", "1:0:2", "2:2"});
+        persistSettings("0:2:1:0:2:1");
+        DeviceStateRotationLockSettingsManager manager =
+                new DeviceStateRotationLockSettingsManager(mMockContext, mFakeSecureSettings);
+
+        mExpect.that(manager.getRotationLockSetting(0)).isEqualTo(2);
+        mExpect.that(manager.getRotationLockSetting(1)).isEqualTo(1);
+        mExpect.that(manager.getRotationLockSetting(2)).isEqualTo(1);
+    }
+
+    private void persistSettings(String value) {
+        mFakeSecureSettings.putStringForUser(
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                value,
+                UserHandle.USER_CURRENT);
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index c9d840a..6a5535d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -97,5 +97,8 @@
         Settings.System.TOUCHPAD_NATURAL_SCROLLING,
         Settings.System.TOUCHPAD_TAP_TO_CLICK,
         Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
+        Settings.System.CAMERA_FLASH_NOTIFICATION,
+        Settings.System.SCREEN_FLASH_NOTIFICATION,
+        Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index c2a3ada..3fe12b3 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -16,6 +16,7 @@
 
 package android.provider.settings.validators;
 
+import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
@@ -215,5 +216,8 @@
         VALIDATORS.put(System.UNREAD_NOTIFICATION_DOT_INDICATOR, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.AUTO_LAUNCH_MEDIA_CONTROLS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.LOCALE_PREFERENCES, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1a69208..d49627e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2835,6 +2835,15 @@
         dumpSetting(s, p,
                 Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 SystemSettingsProto.Notification.VIBRATION_INTENSITY);
+        dumpSetting(s, p,
+                Settings.System.CAMERA_FLASH_NOTIFICATION,
+                SystemSettingsProto.Notification.CAMERA_FLASH_NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_FLASH_NOTIFICATION,
+                SystemSettingsProto.Notification.SCREEN_FLASH_NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
+                SystemSettingsProto.Notification.SCREEN_FLASH_NOTIFICATION_COLOR_GLOBAL);
         // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
         p.end(notificationToken);
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 418011a..27c8cdf 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3056,11 +3056,11 @@
             final int key = makeKey(type, userId);
 
             boolean success = false;
-            boolean isNewSetting = false;
+            boolean wasUnsetNonPredefinedSetting = false;
             SettingsState settingsState = peekSettingsStateLocked(key);
             if (settingsState != null) {
-                if (!settingsState.hasSetting(name)) {
-                    isNewSetting = true;
+                if (!isSettingPreDefined(name, type) && !settingsState.hasSetting(name)) {
+                    wasUnsetNonPredefinedSetting = true;
                 }
                 success = settingsState.insertSettingLocked(name, value,
                         tag, makeDefault, forceNonSystemPackage, packageName,
@@ -3073,9 +3073,9 @@
 
             if (forceNotify || success) {
                 notifyForSettingsChange(key, name);
-                if (isNewSetting && !isSettingPreDefined(name, type)) {
-                    // Increment the generation number for all null settings because a new
-                    // non-predefined setting has been inserted
+                if (wasUnsetNonPredefinedSetting) {
+                    // Increment the generation number for all non-predefined, unset settings,
+                    // because a new non-predefined setting has been inserted
                     mGenerationRegistry.incrementGenerationForUnsetSettings(key);
                 }
             }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 17a94b86..296c2ae 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -419,7 +419,7 @@
         internal val delegate: AnimationDelegate
 
         init {
-            delegate = AnimationDelegate(controller, callback, launchAnimator, listener)
+            delegate = AnimationDelegate(controller, callback, listener, launchAnimator)
         }
 
         @BinderThread
@@ -446,10 +446,10 @@
     constructor(
         private val controller: Controller,
         private val callback: Callback,
-        /** The animator to use to animate the window launch. */
-        private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR,
         /** Listener for animation lifecycle events. */
-        private val listener: Listener? = null
+        private val listener: Listener? = null,
+        /** The animator to use to animate the window launch. */
+        private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR
     ) : RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> {
         private val launchContainer = controller.launchContainer
         private val context = launchContainer.context
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e65c327..8f90724 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -782,7 +782,7 @@
     <!-- Duration in milliseconds of the dream in complications fade-in animation. -->
     <integer name="config_dreamOverlayInComplicationsDurationMs">250</integer>
     <!-- Duration in milliseconds of the y-translation animation when entering a dream -->
-    <integer name="config_dreamOverlayInTranslationYDurationMs">917</integer>
+    <integer name="config_dreamOverlayInTranslationYDurationMs">1167</integer>
 
     <!-- Delay in milliseconds before switching to the dock user and dreaming if a secondary user is
     active when the device is locked and docked. 0 indicates disabled. Default is 1 minute. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index aba3fc4..0f2ce44 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -196,9 +196,6 @@
     <!-- Increased height of a small notification in the status bar -->
     <dimen name="notification_min_height_increased">146dp</dimen>
 
-    <!-- Increased height of a collapsed media notification in the status bar -->
-    <dimen name="notification_min_height_media">160dp</dimen>
-
     <!-- Height of a small notification in the status bar which was used before android N -->
     <dimen name="notification_min_height_legacy">64dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
index 54f933a..53a421d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.settings.SystemSettings
+import kotlin.math.roundToInt
 
 /** The Dialog that contains a seekbar for changing the font size. */
 class FontScalingDialog(context: Context, private val systemSettings: SystemSettings) :
@@ -56,6 +57,16 @@
         doneButton = requireViewById(com.android.internal.R.id.button1)
         seekBarWithIconButtonsView = requireViewById(R.id.font_scaling_slider)
 
+        val labelArray = arrayOfNulls<String>(strEntryValues.size)
+        for (i in strEntryValues.indices) {
+            labelArray[i] =
+                context.resources.getString(
+                    com.android.settingslib.R.string.font_scale_percentage,
+                    (strEntryValues[i].toFloat() * 100).roundToInt()
+                )
+        }
+        seekBarWithIconButtonsView.setProgressStateLabels(labelArray)
+
         seekBarWithIconButtonsView.setMax((strEntryValues).size - 1)
 
         val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, 1.0f)
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 2501be9..e049ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -351,9 +351,20 @@
     }
 
     private void animateFromMinimized() {
-        mIsMinimized = false;
-        setExpandedView();
-        animateIn();
+        if (mEnterAnimator != null && mEnterAnimator.isRunning()) {
+            mEnterAnimator.cancel();
+        }
+        mEnterAnimator = mView.getMinimizedFadeoutAnimation();
+        mEnterAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mIsMinimized = false;
+                setExpandedView();
+                animateIn();
+            }
+        });
+        mEnterAnimator.start();
     }
 
     private String getAccessibilityAnnouncement(ClipboardModel.Type type) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index f372bb4..28c57d3 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -21,6 +21,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
@@ -286,6 +287,20 @@
         mActionChips.clear();
     }
 
+    Animator getMinimizedFadeoutAnimation() {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(mMinimizedPreview, "alpha", 1, 0);
+        anim.setDuration(66);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mMinimizedPreview.setVisibility(View.GONE);
+                mMinimizedPreview.setAlpha(1);
+            }
+        });
+        return anim;
+    }
+
     Animator getEnterAnimation() {
         if (mAccessibilityManager.isEnabled()) {
             mDismissButton.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
index 24f6296..de3a990 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
@@ -45,6 +45,7 @@
     private SeekBar mSeekbar;
 
     private SeekBarChangeListener mSeekBarListener = new SeekBarChangeListener();
+    private String[] mStateLabels = null;
 
     public SeekBarWithIconButtonsView(Context context) {
         this(context, null);
@@ -132,6 +133,30 @@
     }
 
     /**
+     * Stores the String array we would like to use for describing the state of seekbar progress
+     * and updates the state description with current progress.
+     *
+     * @param labels The state descriptions to be announced for each progress.
+     */
+    public void setProgressStateLabels(String[] labels) {
+        mStateLabels = labels;
+        if (mStateLabels != null) {
+            setSeekbarStateDescription();
+        }
+    }
+
+    /**
+     * Sets the state of seekbar based on current progress. The progress of seekbar is
+     * corresponding to the index of the string array. If the progress is larger than or equals
+     * to the length of the array, the state description is set to an empty string.
+     */
+    private void setSeekbarStateDescription() {
+        mSeekbar.setStateDescription(
+                (mSeekbar.getProgress() < mStateLabels.length)
+                        ? mStateLabels[mSeekbar.getProgress()] : "");
+    }
+
+    /**
      * Sets a onSeekbarChangeListener to the seekbar in the layout.
      * We update the Start Icon and End Icon if needed when the seekbar progress is changed.
      */
@@ -173,6 +198,9 @@
 
         @Override
         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            if (mStateLabels != null) {
+                setSeekbarStateDescription();
+            }
             if (mOnSeekBarChangeListener != null) {
                 mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index ca1cef3..d0a92f0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -43,7 +43,6 @@
 import javax.inject.Inject
 import javax.inject.Named
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.launch
 
@@ -131,9 +130,17 @@
         }
     }
 
-    /** Starts the dream content and dream overlay entry animations. */
+    /**
+     * Starts the dream content and dream overlay entry animations.
+     *
+     * @param downwards if true, the entry animation translations downwards into position rather
+     *   than upwards.
+     */
     @JvmOverloads
-    fun startEntryAnimations(animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
+    fun startEntryAnimations(
+        downwards: Boolean,
+        animatorBuilder: () -> AnimatorSet = { AnimatorSet() }
+    ) {
         cancelAnimations()
 
         mAnimator =
@@ -153,7 +160,7 @@
                         interpolator = Interpolators.LINEAR
                     ),
                     translationYAnimator(
-                        from = mDreamInTranslationYDistance.toFloat(),
+                        from = mDreamInTranslationYDistance.toFloat() * (if (downwards) -1 else 1),
                         to = 0f,
                         durationMs = mDreamInTranslationYDurationMs,
                         interpolator = Interpolators.EMPHASIZED_DECELERATE
@@ -167,6 +174,71 @@
             }
     }
 
+    /**
+     * Starts the dream content and dream overlay exit animations.
+     *
+     * This should only be used when the low light dream is entering, animations to/from other SysUI
+     * views is controlled by `transitionViewModel`.
+     */
+    // TODO(b/256916668): integrate with the keyguard transition model once dream surfaces work is
+    // done.
+    @JvmOverloads
+    fun startExitAnimations(animatorBuilder: () -> AnimatorSet = { AnimatorSet() }): Animator {
+        cancelAnimations()
+
+        mAnimator =
+            animatorBuilder().apply {
+                playTogether(
+                    translationYAnimator(
+                        from = 0f,
+                        to = -mDreamInTranslationYDistance.toFloat(),
+                        durationMs = mDreamInTranslationYDurationMs,
+                        delayMs = 0,
+                        interpolator = Interpolators.EMPHASIZED
+                    ),
+                    alphaAnimator(
+                            from =
+                                mCurrentAlphaAtPosition.getOrDefault(
+                                    key = POSITION_BOTTOM,
+                                    defaultValue = 1f
+                                ),
+                            to = 0f,
+                            durationMs = mDreamInComplicationsAnimDurationMs,
+                            delayMs = 0,
+                            positions = POSITION_BOTTOM
+                        )
+                        .apply {
+                            doOnEnd {
+                                // The logical end of the animation is once the alpha and blur
+                                // animations finish, end the animation so that any listeners are
+                                // notified. The Y translation animation is much longer than all of
+                                // the other animations due to how the spec is defined, but is not
+                                // expected to run to completion.
+                                mAnimator?.end()
+                            }
+                        },
+                    alphaAnimator(
+                        from =
+                            mCurrentAlphaAtPosition.getOrDefault(
+                                key = POSITION_TOP,
+                                defaultValue = 1f
+                            ),
+                        to = 0f,
+                        durationMs = mDreamInComplicationsAnimDurationMs,
+                        delayMs = 0,
+                        positions = POSITION_TOP
+                    )
+                )
+                doOnEnd {
+                    mAnimator = null
+                    mOverlayStateController.setExitAnimationsRunning(false)
+                }
+                start()
+            }
+        mOverlayStateController.setExitAnimationsRunning(true)
+        return mAnimator as AnimatorSet
+    }
+
     /** Starts the dream content and dream overlay exit animations. */
     fun wakeUp(doneCallback: Runnable, executor: DelayableExecutor) {
         cancelAnimations()
@@ -182,19 +254,6 @@
             }
     }
 
-    /**
-     * Ends the dream content and dream overlay animations, if they're currently running.
-     *
-     * @see [AnimatorSet.end]
-     */
-    fun endAnimations() {
-        mAnimator =
-            mAnimator?.let {
-                it.end()
-                null
-            }
-    }
-
     private fun blurAnimator(
         view: View,
         fromBlurRadius: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 50cfb6a..4b478cd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -23,6 +23,7 @@
 import static com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_BOTTOM;
 import static com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_TOP;
 
+import android.animation.Animator;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.util.MathUtils;
@@ -31,6 +32,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.dream.lowlight.LowLightTransitionCoordinator;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -54,11 +56,14 @@
  * View controller for {@link DreamOverlayContainerView}.
  */
 @DreamOverlayComponent.DreamOverlayScope
-public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> {
+public class DreamOverlayContainerViewController extends
+        ViewController<DreamOverlayContainerView> implements
+        LowLightTransitionCoordinator.LowLightEnterListener {
     private final DreamOverlayStatusBarViewController mStatusBarViewController;
     private final BlurUtils mBlurUtils;
     private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
     private final DreamOverlayStateController mStateController;
+    private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
 
     private final ComplicationHostViewController mComplicationHostViewController;
 
@@ -143,19 +148,18 @@
             };
 
     /**
-     * If true, overlay entry animations should be skipped once.
-     *
-     * This is turned on when exiting low light and should be turned off once the entry animations
-     * are skipped once.
+     * If {@code true}, the dream has just transitioned from the low light dream back to the user
+     * dream and we should play an entry animation where the overlay slides in downwards from the
+     * top instead of the typicla slide in upwards from the bottom.
      */
-    private boolean mSkipEntryAnimations;
+    private boolean mExitingLowLight;
 
     private final DreamOverlayStateController.Callback
             mDreamOverlayStateCallback =
             new DreamOverlayStateController.Callback() {
                 @Override
                 public void onExitLowLight() {
-                    mSkipEntryAnimations = true;
+                    mExitingLowLight = true;
                 }
             };
 
@@ -165,6 +169,7 @@
             ComplicationHostViewController complicationHostViewController,
             @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
             DreamOverlayStatusBarViewController statusBarViewController,
+            LowLightTransitionCoordinator lowLightTransitionCoordinator,
             BlurUtils blurUtils,
             @Main Handler handler,
             @Main Resources resources,
@@ -182,6 +187,7 @@
         mBlurUtils = blurUtils;
         mDreamOverlayAnimationsController = animationsController;
         mStateController = stateController;
+        mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
 
         mBouncerlessScrimController = bouncerlessScrimController;
         mBouncerlessScrimController.addCallback(mBouncerlessExpansionCallback);
@@ -208,6 +214,7 @@
         mStatusBarViewController.init();
         mComplicationHostViewController.init();
         mDreamOverlayAnimationsController.init(mView);
+        mLowLightTransitionCoordinator.setLowLightEnterListener(this);
     }
 
     @Override
@@ -219,14 +226,10 @@
 
         // Start dream entry animations. Skip animations for low light clock.
         if (!mStateController.isLowLightActive()) {
-            mDreamOverlayAnimationsController.startEntryAnimations();
-
-            if (mSkipEntryAnimations) {
-                // If we're transitioning from the low light dream back to the user dream, skip the
-                // overlay animations and show immediately.
-                mDreamOverlayAnimationsController.endAnimations();
-                mSkipEntryAnimations = false;
-            }
+            // If this is transitioning from the low light dream to the user dream, the overlay
+            // should translate in downwards instead of upwards.
+            mDreamOverlayAnimationsController.startEntryAnimations(mExitingLowLight);
+            mExitingLowLight = false;
         }
     }
 
@@ -310,4 +313,12 @@
 
         mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
     }
+
+    @Override
+    public Animator onBeforeEnterLowLight() {
+        // Return the animator so that the transition coordinator waits for the overlay exit
+        // animations to finish before entering low light, as otherwise the default DreamActivity
+        // animation plays immediately and there's no time for this animation to play.
+        return mDreamOverlayAnimationsController.startExitAnimations();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
index a2e11b2..24e90f0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -22,14 +22,18 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Debug;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.lifecycle.LifecycleOwner;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.util.ViewController;
+import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.Collection;
 import java.util.HashMap;
@@ -54,6 +58,8 @@
     private final LifecycleOwner mLifecycleOwner;
     private final ComplicationCollectionViewModel mComplicationCollectionViewModel;
     private final HashMap<ComplicationId, Complication.ViewHolder> mComplications = new HashMap<>();
+    @VisibleForTesting
+    boolean mIsAnimationEnabled;
 
     // Whether dream entry animations are finished.
     private boolean mEntryAnimationsFinished = false;
@@ -64,7 +70,8 @@
             ComplicationLayoutEngine layoutEngine,
             DreamOverlayStateController dreamOverlayStateController,
             LifecycleOwner lifecycleOwner,
-            @Named(SCOPED_COMPLICATIONS_MODEL) ComplicationCollectionViewModel viewModel) {
+            @Named(SCOPED_COMPLICATIONS_MODEL) ComplicationCollectionViewModel viewModel,
+            SecureSettings secureSettings) {
         super(view);
         mLayoutEngine = layoutEngine;
         mLifecycleOwner = lifecycleOwner;
@@ -78,6 +85,10 @@
                         mDreamOverlayStateController.areEntryAnimationsFinished();
             }
         });
+
+        // Whether animations are enabled.
+        mIsAnimationEnabled = secureSettings.getFloatForUser(
+                Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, UserHandle.USER_CURRENT) != 0.0f;
     }
 
     @Override
@@ -148,7 +159,7 @@
 
                     // Complications to be added before dream entry animations are finished are set
                     // to invisible and are animated in.
-                    if (!mEntryAnimationsFinished) {
+                    if (!mEntryAnimationsFinished && mIsAnimationEnabled) {
                         view.setVisibility(View.INVISIBLE);
                     }
                     mComplications.put(id, viewHolder);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 95b37ac..9a6d90a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -71,8 +71,12 @@
     val NOTIFICATION_MEMORY_MONITOR_ENABLED =
         releasedFlag(112, "notification_memory_monitor_enabled")
 
+    /**
+     * This flag is server-controlled and should stay as [unreleasedFlag] since we never want to
+     * enable it on release builds.
+     */
     val NOTIFICATION_MEMORY_LOGGING_ENABLED =
-        unreleasedFlag(119, "notification_memory_logging_enabled", teamfood = true)
+        unreleasedFlag(119, "notification_memory_logging_enabled")
 
     // TODO(b/254512731): Tracking Bug
     @JvmField val NOTIFICATION_DISMISSAL_FADE = releasedFlag(113, "notification_dismissal_fade")
@@ -104,6 +108,10 @@
     val NOTIFICATION_ANIMATE_BIG_PICTURE =
         releasedFlag(120, "notification_animate_big_picture", teamfood = true)
 
+    @JvmField
+    val ANIMATED_NOTIFICATION_SHADE_INSETS =
+        unreleasedFlag(270682168, "animated_notification_shade_insets", teamfood = true)
+
     // 200 - keyguard/lockscreen
     // ** Flag retired **
     // public static final BooleanFlag KEYGUARD_LAYOUT =
@@ -469,7 +477,7 @@
     @Keep
     @JvmField
     val ENABLE_PIP_APP_ICON_OVERLAY =
-        sysPropBooleanFlag(1115, "persist.wm.debug.enable_pip_app_icon_overlay", default = false)
+        sysPropBooleanFlag(1115, "persist.wm.debug.enable_pip_app_icon_overlay", default = true)
 
     // 1200 - predictive back
     @Keep
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/DeviceStateAutoRotationLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/DeviceStateAutoRotationLog.java
new file mode 100644
index 0000000..beb725e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/DeviceStateAutoRotationLog.java
@@ -0,0 +1,30 @@
+/*
+ * 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 java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface DeviceStateAutoRotationLog {
+}
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 ca1ed1f..d246b35e 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -370,6 +370,16 @@
     }
 
     /**
+     * Provides a {@link LogBuffer} for Device State Auto-Rotation logs.
+     */
+    @Provides
+    @SysUISingleton
+    @DeviceStateAutoRotationLog
+    public static LogBuffer provideDeviceStateAutoRotationLogBuffer(LogBufferFactory factory) {
+        return factory.create("DeviceStateAutoRotationLog", 100);
+    }
+
+    /**
      * Provides a {@link LogBuffer} for bluetooth-related logs.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 680a8b6..67d3be4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -197,7 +197,6 @@
 
     private val configListener =
         object : ConfigurationController.ConfigurationListener {
-            var lastOrientation = -1
 
             override fun onDensityOrFontScaleChanged() {
                 // System font changes should only happen when UMO is offscreen or a flicker may
@@ -214,13 +213,6 @@
             override fun onConfigChanged(newConfig: Configuration?) {
                 if (newConfig == null) return
                 isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
-                val newOrientation = newConfig.orientation
-                if (lastOrientation != newOrientation) {
-                    // The players actually depend on the orientation possibly, so we have to
-                    // recreate them (at least on large screen devices)
-                    lastOrientation = newOrientation
-                    updatePlayers(recreateMedia = true)
-                }
             }
 
             override fun onUiModeChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 0788e61..b4724dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -154,9 +154,11 @@
             return transitionLayout?.translationY ?: 0.0f
         }
 
-    /** A callback for RTL config changes */
+    /** A callback for config changes */
     private val configurationListener =
         object : ConfigurationController.ConfigurationListener {
+            var lastOrientation = -1
+
             override fun onConfigChanged(newConfig: Configuration?) {
                 // Because the TransitionLayout is not always attached (and calculates/caches layout
                 // results regardless of attach state), we have to force the layoutDirection of the
@@ -169,6 +171,13 @@
                         transitionLayout?.layoutDirection = layoutDirection
                         refreshState()
                     }
+                    val newOrientation = newConfig.orientation
+                    if (lastOrientation != newOrientation) {
+                        // Layout dimensions are possibly changing, so we need to update them. (at
+                        // least on large screen devices)
+                        lastOrientation = newOrientation
+                        loadLayoutForType(type)
+                    }
                 }
             }
         }
@@ -195,13 +204,14 @@
      * The expanded constraint set used to render a expanded player. If it is modified, make sure to
      * call [refreshState]
      */
-    val collapsedLayout = ConstraintSet()
-
+    var collapsedLayout = ConstraintSet()
+        @VisibleForTesting set
     /**
      * The expanded constraint set used to render a collapsed player. If it is modified, make sure
      * to call [refreshState]
      */
-    val expandedLayout = ConstraintSet()
+    var expandedLayout = ConstraintSet()
+        @VisibleForTesting set
 
     /** Whether the guts are visible for the associated player. */
     var isGutsVisible = false
@@ -483,7 +493,7 @@
      */
     fun attach(transitionLayout: TransitionLayout, type: TYPE) =
         traceSection("MediaViewController#attach") {
-            updateMediaViewControllerType(type)
+            loadLayoutForType(type)
             logger.logMediaLocation("attach $type", currentStartLocation, currentEndLocation)
             this.transitionLayout = transitionLayout
             layoutController.attach(transitionLayout)
@@ -641,7 +651,7 @@
         return result
     }
 
-    private fun updateMediaViewControllerType(type: TYPE) {
+    private fun loadLayoutForType(type: TYPE) {
         this.type = type
 
         // These XML resources contain ConstraintSets that will apply to this player type's layout
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index a233cdc..006cedf 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -164,8 +164,6 @@
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
-import dagger.Lazy;
-
 import java.io.PrintWriter;
 import java.util.Locale;
 import java.util.Map;
@@ -175,6 +173,8 @@
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * Contains logic for a navigation bar view.
  */
@@ -251,6 +251,12 @@
 
     private boolean mTransientShown;
     private boolean mTransientShownFromGestureOnSystemBar;
+    /**
+     * This is to indicate whether the navigation bar button is forced visible. This is true
+     * when the setup wizard is on display. When that happens, the window frame should be provided
+     * as insets size directly.
+     */
+    private boolean mIsButtonForceVisible;
     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
     private LightBarController mLightBarController;
     private final LightBarController mMainLightBarController;
@@ -664,7 +670,8 @@
         mView.setTouchHandler(mTouchHandler);
         setNavBarMode(mNavBarMode);
         mEdgeBackGestureHandler.setStateChangeCallback(mView::updateStates);
-        mEdgeBackGestureHandler.setButtonForcedVisibleChangeCallback((forceVisible) -> {
+        mEdgeBackGestureHandler.setButtonForceVisibleChangeCallback((forceVisible) -> {
+            mIsButtonForceVisible = forceVisible;
             repositionNavigationBar(mCurrentRotation);
         });
         mNavigationBarTransitions.addListener(this::onBarTransition);
@@ -1703,7 +1710,7 @@
 
     private InsetsFrameProvider[] getInsetsFrameProvider(int insetsHeight, Context userContext) {
         final InsetsFrameProvider navBarProvider;
-        if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) {
+        if (insetsHeight != -1 && !mIsButtonForceVisible) {
             navBarProvider = new InsetsFrameProvider(
                     ITYPE_NAVIGATION_BAR, Insets.of(0, 0, 0, insetsHeight));
             // Use window frame for IME.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
index f335733..70040c7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -2,19 +2,15 @@
 
 import android.content.Context
 import android.content.res.Configuration
-import android.content.res.TypedArray
 import android.graphics.Canvas
 import android.graphics.Paint
 import android.graphics.Path
 import android.graphics.RectF
 import android.util.MathUtils.min
-import android.util.TypedValue
 import android.view.View
-import androidx.appcompat.view.ContextThemeWrapper
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
-import com.android.internal.R.style.Theme_DeviceDefault
 import com.android.internal.util.LatencyTracker
 import com.android.settingslib.Utils
 import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener
@@ -159,26 +155,21 @@
         val isDeviceInNightTheme = resources.configuration.uiMode and
                 Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
 
-        val colorControlActivated = ContextThemeWrapper(context, Theme_DeviceDefault)
-                .run {
-                    val typedValue = TypedValue()
-                    val a: TypedArray = obtainStyledAttributes(typedValue.data,
-                            intArrayOf(android.R.attr.colorControlActivated))
-                    val color = a.getColor(0, 0)
-                    a.recycle()
-                    color
+        arrowPaint.color = Utils.getColorAttrDefaultColor(context,
+                if (isDeviceInNightTheme) {
+                    com.android.internal.R.attr.colorAccentPrimary
+                } else {
+                    com.android.internal.R.attr.textColorPrimary
                 }
+        )
 
-        val colorPrimary =
-                Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
-
-        arrowPaint.color = Utils.getColorAccentDefaultColor(context)
-
-        arrowBackgroundPaint.color = if (isDeviceInNightTheme) {
-            colorPrimary
-        } else {
-            colorControlActivated
-        }
+        arrowBackgroundPaint.color = Utils.getColorAttrDefaultColor(context,
+                if (isDeviceInNightTheme) {
+                    com.android.internal.R.attr.materialColorOnSecondary
+                } else {
+                    com.android.internal.R.attr.colorAccentSecondary
+                }
+        )
     }
 
     inner class AnimatedFloat(
@@ -414,9 +405,9 @@
     ) {
         horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation)
         scale.updateRestingPosition(restingParams.scale)
-        arrowAlpha.updateRestingPosition(restingParams.arrowDimens.alpha)
         backgroundAlpha.updateRestingPosition(restingParams.backgroundDimens.alpha)
 
+        arrowAlpha.updateRestingPosition(restingParams.arrowDimens.alpha, animate)
         arrowLength.updateRestingPosition(restingParams.arrowDimens.length, animate)
         arrowHeight.updateRestingPosition(restingParams.arrowDimens.height, animate)
         scalePivotX.updateRestingPosition(restingParams.backgroundDimens.width, animate)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index f409b23..80ed08c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -55,12 +55,12 @@
 
 internal const val MIN_DURATION_ACTIVE_ANIMATION = 300L
 private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
-private const val MIN_DURATION_COMMITTED_ANIMATION = 200L
+private const val MIN_DURATION_COMMITTED_ANIMATION = 120L
 private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
 private const val MIN_DURATION_CONSIDERED_AS_FLING = 100L
 
 private const val FAILSAFE_DELAY_MS = 350L
-private const val POP_ON_FLING_DELAY = 160L
+private const val POP_ON_FLING_DELAY = 140L
 
 internal val VIBRATE_ACTIVATED_EFFECT =
         VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
@@ -148,8 +148,6 @@
     private var gestureSinceActionDown = 0L
     private var gestureEntryTime = 0L
     private var gestureActiveTime = 0L
-    private var gestureInactiveOrEntryTime = 0L
-    private var gestureArrowStrokeVisibleTime = 0L
 
     private val elapsedTimeSinceActionDown
         get() = SystemClock.uptimeMillis() - gestureSinceActionDown
@@ -441,34 +439,44 @@
 
         updateArrowStateOnMove(yTranslation, xTranslation)
 
-        when (currentState) {
-            GestureState.ACTIVE -> {
-                stretchActiveBackIndicator(fullScreenProgress(xTranslation))
-            }
-            GestureState.ENTRY -> {
-                val progress = staticThresholdProgress(xTranslation)
-                stretchEntryBackIndicator(progress)
-
-                params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
-                    mView.popArrowAlpha(0f, it.value)
-                }
-            }
-            GestureState.INACTIVE -> {
-                val progress = reactivationThresholdProgress(totalTouchDelta)
-                stretchInactiveBackIndicator(progress)
-
-                params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
-                    gestureArrowStrokeVisibleTime = SystemClock.uptimeMillis()
-                    mView.popArrowAlpha(0f, it.value)
-                }
-            }
-            else -> {}
+        val gestureProgress = when (currentState) {
+            GestureState.ACTIVE -> fullScreenProgress(xTranslation)
+            GestureState.ENTRY -> staticThresholdProgress(xTranslation)
+            GestureState.INACTIVE -> reactivationThresholdProgress(totalTouchDelta)
+            else -> null
         }
 
-        // set y translation
+        gestureProgress?.let {
+            when (currentState) {
+                GestureState.ACTIVE -> stretchActiveBackIndicator(gestureProgress)
+                GestureState.ENTRY -> stretchEntryBackIndicator(gestureProgress)
+                GestureState.INACTIVE -> stretchInactiveBackIndicator(gestureProgress)
+                else -> {}
+            }
+        }
+
+        setArrowStrokeAlpha(gestureProgress)
         setVerticalTranslation(yOffset)
     }
 
+    private fun setArrowStrokeAlpha(gestureProgress: Float?) {
+        val strokeAlphaProgress = when (currentState) {
+            GestureState.ENTRY -> gestureProgress
+            GestureState.INACTIVE -> gestureProgress
+            GestureState.ACTIVE,
+            GestureState.FLUNG,
+            GestureState.COMMITTED -> 1f
+            GestureState.CANCELLED,
+            GestureState.GONE -> 0f
+        }
+
+        strokeAlphaProgress?.let { progress ->
+            params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
+                mView.popArrowAlpha(0f, it.value)
+            }
+        }
+    }
+
     private fun setVerticalTranslation(yOffset: Float) {
         val yTranslation = abs(yOffset)
         val maxYOffset = (mView.height - params.entryIndicator.backgroundDimens.height) / 2f
@@ -599,7 +607,7 @@
 
     private fun isFlungAwayFromEdge(endX: Float, startX: Float = touchDeltaStartX): Boolean {
         val minDistanceConsideredForFling = ViewConfiguration.get(context).scaledTouchSlop
-        val flingDistance = abs(endX - startX)
+        val flingDistance = if (mView.isLeftPanel) endX - startX else startX - endX
         val isPastFlingVelocity = isDragAwayFromEdge(
                 velocityPxPerSecThreshold =
                 ViewConfiguration.get(context).scaledMinimumFlingVelocity)
@@ -764,7 +772,7 @@
                             GestureState.ENTRY,
                             GestureState.INACTIVE -> params.entryIndicator.arrowDimens
                             GestureState.ACTIVE -> params.activeIndicator.arrowDimens
-                            GestureState.FLUNG,
+                            GestureState.FLUNG -> params.flungIndicator.arrowDimens
                             GestureState.COMMITTED -> params.committedIndicator.arrowDimens
                             GestureState.CANCELLED -> params.cancelledIndicator.arrowDimens
                         },
@@ -825,7 +833,6 @@
 
                 updateRestingArrowDimens()
                 gestureEntryTime = SystemClock.uptimeMillis()
-                gestureInactiveOrEntryTime = SystemClock.uptimeMillis()
             }
             GestureState.ACTIVE -> {
                 previousXTranslationOnActiveOffset = previousXTranslation
@@ -857,7 +864,13 @@
             }
 
             GestureState.INACTIVE -> {
-                gestureInactiveOrEntryTime = SystemClock.uptimeMillis()
+
+                // Typically entering INACTIVE means
+                // totalTouchDelta <= deactivationSwipeTriggerThreshold
+                // but because we can also independently enter this state
+                // if touch Y >> touch X, we force it to deactivationSwipeTriggerThreshold
+                // so that gesture progress in this state is consistent regardless of entry
+                totalTouchDelta = params.deactivationSwipeTriggerThreshold
 
                 val startingVelocity = convertVelocityToSpringStartingVelocity(
                         valueOnFastVelocity = -1.05f,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 163b6fa..f28c275 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -178,7 +178,7 @@
     private final OverviewProxyService mOverviewProxyService;
     private final SysUiState mSysUiState;
     private Runnable mStateChangeCallback;
-    private Consumer<Boolean> mButtonForcedVisibleCallback;
+    private Consumer<Boolean> mButtonForceVisibleCallback;
 
     private final PluginManager mPluginManager;
     private final ProtoTracer mProtoTracer;
@@ -246,7 +246,7 @@
     private boolean mGestureBlockingActivityRunning;
     private boolean mIsNewBackAffordanceEnabled;
     private boolean mIsTrackpadGestureBackEnabled;
-    private boolean mIsButtonForcedVisible;
+    private boolean mIsButtonForceVisible;
 
     private InputMonitor mInputMonitor;
     private InputChannelCompat.InputEventReceiver mInputEventReceiver;
@@ -413,8 +413,8 @@
         mStateChangeCallback = callback;
     }
 
-    public void setButtonForcedVisibleChangeCallback(Consumer<Boolean> callback) {
-        mButtonForcedVisibleCallback = callback;
+    public void setButtonForceVisibleChangeCallback(Consumer<Boolean> callback) {
+        mButtonForceVisibleCallback = callback;
     }
 
     public int getEdgeWidthLeft() {
@@ -429,14 +429,13 @@
         Resources res = mNavigationModeController.getCurrentUserContext().getResources();
         mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
         mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
-        final boolean previousForcedVisible = mIsButtonForcedVisible;
-        mIsButtonForcedVisible =
+        final boolean previousForceVisible = mIsButtonForceVisible;
+        mIsButtonForceVisible =
                 mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
-        if (previousForcedVisible != mIsButtonForcedVisible
-                && mButtonForcedVisibleCallback != null) {
-            mButtonForcedVisibleCallback.accept(mIsButtonForcedVisible);
+        if (previousForceVisible != mIsButtonForceVisible && mButtonForceVisibleCallback != null) {
+            mButtonForceVisibleCallback.accept(mIsButtonForceVisible);
         }
-        mIsBackGestureAllowed = !mIsButtonForcedVisible;
+        mIsBackGestureAllowed = !mIsButtonForceVisible;
 
         final DisplayMetrics dm = res.getDisplayMetrics();
         final float defaultGestureHeight = res.getDimension(
@@ -636,10 +635,6 @@
         return mIsEnabled && mIsBackGestureAllowed;
     }
 
-    public boolean isButtonForcedVisible() {
-        return mIsButtonForcedVisible;
-    }
-
     /**
      * Update the PiP bounds, used for exclusion calculation.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index 0c00022..d46333a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -9,8 +9,8 @@
 data class EdgePanelParams(private var resources: Resources) {
 
     data class ArrowDimens(
-            val length: Float = 0f,
-            val height: Float = 0f,
+            val length: Float? = 0f,
+            val height: Float? = 0f,
             val alpha: Float = 0f,
             var alphaSpring: SpringForce? = null,
             val heightSpring: SpringForce? = null,
@@ -139,17 +139,17 @@
 
         entryWidthInterpolator = PathInterpolator(.19f, 1.27f, .71f, .86f)
         entryWidthTowardsEdgeInterpolator = PathInterpolator(1f, -3f, 1f, 1.2f)
-        activeWidthInterpolator = PathInterpolator(.15f, .48f, .46f, .89f)
+        activeWidthInterpolator = PathInterpolator(.32f, 0f, .16f, .94f)
         arrowAngleInterpolator = entryWidthInterpolator
         translationInterpolator = PathInterpolator(0.2f, 1.0f, 1.0f, 1.0f)
         farCornerInterpolator = PathInterpolator(.03f, .19f, .14f, 1.09f)
         edgeCornerInterpolator = PathInterpolator(0f, 1.11f, .85f, .84f)
         heightInterpolator = PathInterpolator(1f, .05f, .9f, -0.29f)
 
-        val showArrowOnProgressValue = .2f
+        val showArrowOnProgressValue = .23f
         val showArrowOnProgressValueFactor = 1.05f
 
-        val entryActiveHorizontalTranslationSpring = createSpring(675f, 0.8f)
+        val entryActiveHorizontalTranslationSpring = createSpring(800f, 0.8f)
         val activeCommittedArrowLengthSpring = createSpring(1500f, 0.29f)
         val activeCommittedArrowHeightSpring = createSpring(1500f, 0.29f)
         val flungCommittedEdgeCornerSpring = createSpring(10000f, 1f)
@@ -178,7 +178,7 @@
                         height = getDimen(R.dimen.navigation_edge_entry_background_height),
                         edgeCornerRadius = getDimen(R.dimen.navigation_edge_entry_edge_corners),
                         farCornerRadius = getDimen(R.dimen.navigation_edge_entry_far_corners),
-                        alphaSpring = createSpring(900f, 1f),
+                        alphaSpring = createSpring(1100f, 1f),
                         widthSpring = createSpring(450f, 0.65f),
                         heightSpring = createSpring(1500f, 0.45f),
                         farCornerRadiusSpring = createSpring(300f, 0.5f),
@@ -232,7 +232,7 @@
                                 getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
                         farCornerRadius =
                                 getDimen(R.dimen.navigation_edge_pre_threshold_far_corners),
-                        widthSpring = createSpring(200f, 0.65f),
+                        widthSpring = createSpring(250f, 0.65f),
                         heightSpring = createSpring(1500f, 0.45f),
                         farCornerRadiusSpring = createSpring(200f, 1f),
                         edgeCornerRadiusSpring = createSpring(150f, 0.5f),
@@ -244,6 +244,8 @@
                 arrowDimens = activeIndicator.arrowDimens.copy(
                         lengthSpring = activeCommittedArrowLengthSpring,
                         heightSpring = activeCommittedArrowHeightSpring,
+                        length = null,
+                        height = null,
                 ),
                 backgroundDimens = activeIndicator.backgroundDimens.copy(
                         alpha = 0f,
@@ -255,13 +257,15 @@
                         farCornerRadiusSpring = flungCommittedFarCornerSpring,
                 ),
                 scale = 0.85f,
-                scaleSpring = createSpring(650f, 1f),
+                scaleSpring = createSpring(1150f, 1f),
         )
 
         flungIndicator = committedIndicator.copy(
                 arrowDimens = committedIndicator.arrowDimens.copy(
                         lengthSpring = createSpring(850f, 0.46f),
                         heightSpring = createSpring(850f, 0.46f),
+                        length = activeIndicator.arrowDimens.length,
+                        height = activeIndicator.arrowDimens.height
                 ),
                 backgroundDimens = committedIndicator.backgroundDimens.copy(
                         widthSpring = flungCommittedWidthSpring,
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index c65f0aa..5b36e93 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -22,6 +22,9 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+import android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.content.pm.PackageManager
 import android.os.Build
 import android.os.UserManager
@@ -54,8 +57,8 @@
     private val resolver: NoteTaskInfoResolver,
     private val eventLogger: NoteTaskEventLogger,
     private val optionalBubbles: Optional<Bubbles>,
-    private val optionalUserManager: Optional<UserManager>,
-    private val optionalKeyguardManager: Optional<KeyguardManager>,
+    private val userManager: UserManager,
+    private val keyguardManager: KeyguardManager,
     @NoteTaskEnabledKey private val isEnabled: Boolean,
     private val devicePolicyManager: DevicePolicyManager,
     private val userTracker: UserTracker,
@@ -106,8 +109,6 @@
         if (!isEnabled) return
 
         val bubbles = optionalBubbles.getOrNull() ?: return
-        val userManager = optionalUserManager.getOrNull() ?: return
-        val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
 
         // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
         if (!userManager.isUserUnlocked) return
@@ -140,12 +141,13 @@
             logDebug { "onShowNoteTask - start: $info" }
             when (info.launchMode) {
                 is NoteTaskLaunchMode.AppBubble -> {
+                    // TODO(b/267634412, b/268351693): Should use `showOrHideAppBubbleAsUser`
                     bubbles.showOrHideAppBubble(intent)
                     // App bubble logging happens on `onBubbleExpandChanged`.
                     logDebug { "onShowNoteTask - opened as app bubble: $info" }
                 }
                 is NoteTaskLaunchMode.Activity -> {
-                    context.startActivity(intent)
+                    context.startActivityAsUser(intent, userTracker.userHandle)
                     eventLogger.logNoteTaskOpened(info)
                     logDebug { "onShowNoteTask - opened as activity: $info" }
                 }
@@ -198,12 +200,21 @@
 }
 
 private fun createNoteIntent(info: NoteTaskInfo): Intent =
-    Intent(NoteTaskController.ACTION_CREATE_NOTE)
-        .setPackage(info.packageName)
-        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+    Intent(NoteTaskController.ACTION_CREATE_NOTE).apply {
+        setPackage(info.packageName)
+
         // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
         // was used to start it.
-        .putExtra(NoteTaskController.INTENT_EXTRA_USE_STYLUS_MODE, true)
+        putExtra(NoteTaskController.INTENT_EXTRA_USE_STYLUS_MODE, true)
+
+        addFlags(FLAG_ACTIVITY_NEW_TASK)
+        // We should ensure the note experience can be open both as a full screen (lock screen)
+        // and inside the app bubble (contextual). These additional flags will do that.
+        if (info.launchMode == NoteTaskLaunchMode.Activity) {
+            addFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
+            addFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+        }
+    }
 
 private inline fun logDebug(message: () -> String) {
     if (Build.IS_DEBUGGABLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
index 0f75f95..7be491f 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
@@ -17,19 +17,19 @@
 package com.android.systemui.notetask
 
 import android.app.role.RoleManager
-import android.content.Context
 import android.content.pm.PackageManager
 import android.content.pm.PackageManager.ApplicationInfoFlags
 import android.os.UserHandle
 import android.util.Log
+import com.android.systemui.settings.UserTracker
 import javax.inject.Inject
 
 class NoteTaskInfoResolver
 @Inject
 constructor(
-    private val context: Context,
     private val roleManager: RoleManager,
     private val packageManager: PackageManager,
+    private val userTracker: UserTracker,
 ) {
 
     fun resolveInfo(
@@ -38,7 +38,7 @@
         isKeyguardLocked: Boolean = false,
     ): NoteTaskInfo? {
         // TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking.
-        val user = context.user
+        val user = userTracker.userHandle
         val packageName =
             roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user).firstOrNull()
 
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index ba8999c..6278c69 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -17,11 +17,7 @@
 package com.android.systemui.notetask
 
 import android.app.Activity
-import android.app.KeyguardManager
 import android.app.role.RoleManager
-import android.content.Context
-import android.os.UserManager
-import androidx.core.content.getSystemService
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule
@@ -32,7 +28,6 @@
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
-import java.util.Optional
 
 /** Compose all dependencies required by Note Task feature. */
 @Module(includes = [NoteTaskQuickAffordanceModule::class])
@@ -55,15 +50,5 @@
             val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
             return isRoleAvailable && isFeatureEnabled
         }
-
-        @Provides
-        fun provideOptionalKeyguardManager(context: Context): Optional<KeyguardManager> {
-            return Optional.ofNullable(context.getSystemService())
-        }
-
-        @Provides
-        fun provideOptionalUserManager(context: Context): Optional<UserManager> {
-            return Optional.ofNullable(context.getSystemService())
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 7c2536d..d4854e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -328,7 +328,7 @@
             if (listening) {
                 updateDefaultTileAndIcon();
                 refreshState();
-                if (!mServiceManager.isActiveTile()) {
+                if (!mServiceManager.isActiveTile() || !isTileReady()) {
                     mServiceManager.setBindRequested(true);
                     mService.onStartListening();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
index ead3b7b..0b4b7c6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
@@ -45,6 +45,7 @@
         implements ViewTreeObserver.OnComputeInternalInsetsListener {
 
     private static final float VELOCITY_DP_PER_MS = 1;
+    private static final int MAXIMUM_DISMISS_DISTANCE_DP = 400;
 
     private final SwipeDismissHandler mSwipeDismissHandler;
     private final GestureDetector mSwipeDetector;
@@ -347,14 +348,18 @@
             } else {
                 finalX = -1 * getBackgroundRight();
             }
-            float distance = Math.abs(finalX - startX);
+            float distance = Math.min(Math.abs(finalX - startX),
+                    FloatingWindowUtil.dpToPx(mDisplayMetrics, MAXIMUM_DISMISS_DISTANCE_DP));
+            // ensure that view dismisses in the right direction (right in LTR, left in RTL)
+            float distanceVector = Math.copySign(distance, finalX - startX);
 
             anim.addUpdateListener(animation -> {
-                float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
+                float translation = MathUtils.lerp(
+                        startX, startX + distanceVector, animation.getAnimatedFraction());
                 mView.setTranslationX(translation);
                 mView.setAlpha(1 - animation.getAnimatedFraction());
             });
-            anim.setDuration((long) (distance / Math.abs(velocity)));
+            anim.setDuration((long) (Math.abs(distance / velocity)));
             return anim;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index b1987c1..ee9e54a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4720,8 +4720,13 @@
              gesture possible. */
             int pointerIndex = event.findPointerIndex(mTrackingPointer);
             if (pointerIndex < 0) {
-                pointerIndex = 0;
-                mTrackingPointer = event.getPointerId(pointerIndex);
+                if (mTrackingPointer < 0) {
+                    pointerIndex = 0;
+                    mTrackingPointer = event.getPointerId(pointerIndex);
+                } else {
+                    mShadeLog.logMotionEvent(event, "Skipping intercept of multitouch pointer");
+                    return false;
+                }
             }
             final float x = event.getX(pointerIndex);
             final float y = event.getY(pointerIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index c35c5c5..7755003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -93,6 +93,16 @@
     @IntDef({STATE_ICON, STATE_DOT, STATE_HIDDEN})
     public @interface VisibleState { }
 
+    /** Returns a human-readable string of {@link VisibleState}. */
+    public static String getVisibleStateString(@VisibleState int state) {
+        switch(state) {
+            case STATE_ICON: return "ICON";
+            case STATE_DOT: return "DOT";
+            case STATE_HIDDEN: return "HIDDEN";
+            default: return "UNKNOWN";
+        }
+    }
+
     private static final String TAG = "StatusBarIconView";
     private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
             = new FloatProperty<StatusBarIconView>("iconAppearAmount") {
@@ -561,7 +571,8 @@
     @Override
     public String toString() {
         return "StatusBarIconView("
-                + "slot='" + mSlot + " alpha=" + getAlpha() + " icon=" + mIcon
+                + "slot='" + mSlot + "' alpha=" + getAlpha() + " icon=" + mIcon
+                + " visibleState=" + getVisibleStateString(getVisibleState())
                 + " iconColor=#" + Integer.toHexString(mIconColor)
                 + " notification=" + mNotification + ')';
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
new file mode 100644
index 0000000..5ce1db2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.statusbar.notification.collection.coordinator
+
+import android.util.ArrayMap
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.notification.collection.render.NotifGroupController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlin.math.max
+import kotlin.math.min
+
+/** A small coordinator which finds, stores, and applies the closest notification time. */
+@CoordinatorScope
+class GroupWhenCoordinator
+@Inject
+constructor(
+    @Main private val delayableExecutor: DelayableExecutor,
+    private val systemClock: SystemClock
+) : Coordinator {
+
+    private val invalidator = object : Invalidator("GroupWhenCoordinator") {}
+    private val notificationGroupTimes = ArrayMap<GroupEntry, Long>()
+    private var cancelInvalidateListRunnable: Runnable? = null
+
+    private val invalidateListRunnable: Runnable = Runnable {
+        invalidator.invalidateList("future notification invalidation")
+    }
+
+    override fun attach(pipeline: NotifPipeline) {
+        pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilterListener)
+        pipeline.addOnAfterRenderGroupListener(::onAfterRenderGroupListener)
+        pipeline.addPreRenderInvalidator(invalidator)
+    }
+
+    private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) {
+        cancelListInvalidation()
+        notificationGroupTimes.clear()
+
+        val now = systemClock.currentTimeMillis()
+        var closestFutureTime = Long.MAX_VALUE
+        entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
+            val whenMillis = calculateGroupNotificationTime(groupEntry, now)
+            notificationGroupTimes[groupEntry] = whenMillis
+            if (whenMillis > now) {
+                closestFutureTime = min(closestFutureTime, whenMillis)
+            }
+        }
+
+        if (closestFutureTime != Long.MAX_VALUE) {
+            cancelInvalidateListRunnable =
+                delayableExecutor.executeDelayed(invalidateListRunnable, closestFutureTime - now)
+        }
+    }
+
+    private fun cancelListInvalidation() {
+        cancelInvalidateListRunnable?.run()
+        cancelInvalidateListRunnable = null
+    }
+
+    private fun onAfterRenderGroupListener(group: GroupEntry, controller: NotifGroupController) {
+        notificationGroupTimes[group]?.let(controller::setNotificationGroupWhen)
+    }
+
+    private fun calculateGroupNotificationTime(
+        groupEntry: GroupEntry,
+        currentTimeMillis: Long
+    ): Long {
+        var pastTime = Long.MIN_VALUE
+        var futureTime = Long.MAX_VALUE
+        groupEntry.children
+            .asSequence()
+            .mapNotNull { child -> child.sbn.notification.`when`.takeIf { it > 0 } }
+            .forEach { time ->
+                val isInThePast = currentTimeMillis - time > 0
+                if (isInThePast) {
+                    pastTime = max(pastTime, time)
+                } else {
+                    futureTime = min(futureTime, time)
+                }
+            }
+
+        if (pastTime == Long.MIN_VALUE && futureTime == Long.MAX_VALUE) {
+            return checkNotNull(groupEntry.summary).creationTime
+        }
+
+        return if (futureTime != Long.MAX_VALUE) futureTime else pastTime
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 8a82bca..6bb5b92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -31,31 +31,32 @@
 
 @CoordinatorScope
 class NotifCoordinatorsImpl @Inject constructor(
-    notifPipelineFlags: NotifPipelineFlags,
-    dataStoreCoordinator: DataStoreCoordinator,
-    hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
-    hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
-    keyguardCoordinator: KeyguardCoordinator,
-    rankingCoordinator: RankingCoordinator,
-    appOpsCoordinator: AppOpsCoordinator,
-    deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
-    bubbleCoordinator: BubbleCoordinator,
-    headsUpCoordinator: HeadsUpCoordinator,
-    gutsCoordinator: GutsCoordinator,
-    conversationCoordinator: ConversationCoordinator,
-    debugModeCoordinator: DebugModeCoordinator,
-    groupCountCoordinator: GroupCountCoordinator,
-    mediaCoordinator: MediaCoordinator,
-    preparationCoordinator: PreparationCoordinator,
-    remoteInputCoordinator: RemoteInputCoordinator,
-    rowAppearanceCoordinator: RowAppearanceCoordinator,
-    stackCoordinator: StackCoordinator,
-    shadeEventCoordinator: ShadeEventCoordinator,
-    smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
-    viewConfigCoordinator: ViewConfigCoordinator,
-    visualStabilityCoordinator: VisualStabilityCoordinator,
-    sensitiveContentCoordinator: SensitiveContentCoordinator,
-    dismissibilityCoordinator: DismissibilityCoordinator
+        notifPipelineFlags: NotifPipelineFlags,
+        dataStoreCoordinator: DataStoreCoordinator,
+        hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
+        hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
+        keyguardCoordinator: KeyguardCoordinator,
+        rankingCoordinator: RankingCoordinator,
+        appOpsCoordinator: AppOpsCoordinator,
+        deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
+        bubbleCoordinator: BubbleCoordinator,
+        headsUpCoordinator: HeadsUpCoordinator,
+        gutsCoordinator: GutsCoordinator,
+        conversationCoordinator: ConversationCoordinator,
+        debugModeCoordinator: DebugModeCoordinator,
+        groupCountCoordinator: GroupCountCoordinator,
+        groupWhenCoordinator: GroupWhenCoordinator,
+        mediaCoordinator: MediaCoordinator,
+        preparationCoordinator: PreparationCoordinator,
+        remoteInputCoordinator: RemoteInputCoordinator,
+        rowAppearanceCoordinator: RowAppearanceCoordinator,
+        stackCoordinator: StackCoordinator,
+        shadeEventCoordinator: ShadeEventCoordinator,
+        smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
+        viewConfigCoordinator: ViewConfigCoordinator,
+        visualStabilityCoordinator: VisualStabilityCoordinator,
+        sensitiveContentCoordinator: SensitiveContentCoordinator,
+        dismissibilityCoordinator: DismissibilityCoordinator
 ) : NotifCoordinators {
 
     private val mCoordinators: MutableList<Coordinator> = ArrayList()
@@ -82,6 +83,7 @@
         mCoordinators.add(debugModeCoordinator)
         mCoordinators.add(conversationCoordinator)
         mCoordinators.add(groupCountCoordinator)
+        mCoordinators.add(groupWhenCoordinator)
         mCoordinators.add(mediaCoordinator)
         mCoordinators.add(rowAppearanceCoordinator)
         mCoordinators.add(stackCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
index e2edc01..061ef9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
@@ -20,4 +20,7 @@
 interface NotifGroupController {
     /** Set the number of children that this group would have if not for the 8-child max */
     fun setUntruncatedChildCount(untruncatedChildCount: Int)
+
+    /** Set the when value of notification group that reflects most important closest notification time */
+    fun setNotificationGroupWhen(whenMillis: Long)
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 2affa77..6deaa23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -192,7 +192,6 @@
     private int mMaxSmallHeightBeforeS;
     private int mMaxSmallHeight;
     private int mMaxSmallHeightLarge;
-    private int mMaxSmallHeightMedia;
     private int mMaxExpandedHeight;
     private int mIncreasedPaddingBetweenElements;
     private int mNotificationLaunchHeight;
@@ -853,6 +852,19 @@
     }
 
     /**
+     * @see NotificationChildrenContainer#setNotificationGroupWhen(long)
+     */
+    public void setNotificationGroupWhen(long whenMillis) {
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.setNotificationGroupWhen(whenMillis);
+        } else {
+            Log.w(TAG, "setNotificationGroupWhen( whenMillis: " + whenMillis + ")"
+                    + " mIsSummaryWithChildren: false"
+                    + " mChildrenContainer has not been inflated yet.");
+        }
+    }
+
+    /**
      * Called after children have been attached to set the expansion states
      */
     public void resetChildSystemExpandedStates() {
@@ -1774,8 +1786,6 @@
                 R.dimen.notification_min_height);
         mMaxSmallHeightLarge = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_min_height_increased);
-        mMaxSmallHeightMedia = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_min_height_media);
         mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
         mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 2dda6fd..dfc80fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -349,6 +349,15 @@
     }
 
     @Override
+    public void setNotificationGroupWhen(long whenMillis) {
+        if (mView.isSummaryWithChildren()) {
+            mView.setNotificationGroupWhen(whenMillis);
+        } else {
+            Log.w(TAG, "Called setNotificationTime(" + whenMillis + ") on a leaf row");
+        }
+    }
+
+    @Override
     public void setSystemExpanded(boolean systemExpanded) {
         mView.setSystemExpanded(systemExpanded);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 1f664cb..9a777ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -27,6 +27,7 @@
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
+import android.widget.DateTimeView;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -344,6 +345,21 @@
         mTransformationHelper.setVisible(visible);
     }
 
+    /***
+     * Set Notification when value
+     * @param whenMillis
+     */
+    public void setNotificationWhen(long whenMillis) {
+        if (mNotificationHeader == null) {
+            return;
+        }
+
+        final View timeView = mNotificationHeader.findViewById(com.android.internal.R.id.time);
+
+        if (timeView instanceof DateTimeView) {
+            ((DateTimeView) timeView).setTime(whenMillis);
+        }
+    }
     protected void addTransformedViews(View... views) {
         for (View view : views) {
             if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 9b93d7b..40f55bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -296,6 +296,19 @@
     }
 
     /**
+     * Set the notification time in the group so that the view can show the latest event in the UI
+     * appropriately.
+     */
+    public void setNotificationGroupWhen(long whenMillis) {
+        if (mNotificationHeaderWrapper != null) {
+            mNotificationHeaderWrapper.setNotificationWhen(whenMillis);
+        }
+        if (mNotificationHeaderWrapperLowPriority != null) {
+            mNotificationHeaderWrapperLowPriority.setNotificationWhen(whenMillis);
+        }
+    }
+
+    /**
      * Add a child notification to this view.
      *
      * @param row        the row to add
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e09b94b..8d782e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -67,6 +67,7 @@
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AnimationUtils;
@@ -199,6 +200,7 @@
     private final boolean mDebugRemoveAnimation;
     private final boolean mSimplifiedAppearFraction;
     private final boolean mUseRoundnessSourceTypes;
+    private boolean mAnimatedInsets;
 
     private int mContentHeight;
     private float mIntrinsicContentHeight;
@@ -207,7 +209,8 @@
     private int mTopPadding;
     private boolean mAnimateNextTopPaddingChange;
     private int mBottomPadding;
-    private int mBottomInset = 0;
+    @VisibleForTesting
+    int mBottomInset = 0;
     private float mQsExpansionFraction;
     private final int mSplitShadeMinContentHeight;
 
@@ -388,9 +391,33 @@
             }
         }
     };
+
     private boolean mPulsing;
     private boolean mScrollable;
     private View mForcedScroll;
+    private boolean mIsInsetAnimationRunning;
+
+    private final WindowInsetsAnimation.Callback mInsetsCallback =
+            new WindowInsetsAnimation.Callback(
+                    WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
+
+                @Override
+                public void onPrepare(WindowInsetsAnimation animation) {
+                    mIsInsetAnimationRunning = true;
+                }
+
+                @Override
+                public WindowInsets onProgress(WindowInsets windowInsets,
+                        List<WindowInsetsAnimation> list) {
+                    updateBottomInset(windowInsets);
+                    return windowInsets;
+                }
+
+                @Override
+                public void onEnd(WindowInsetsAnimation animation) {
+                    mIsInsetAnimationRunning = false;
+                }
+            };
 
     /**
      * @see #setHideAmount(float, float)
@@ -584,6 +611,7 @@
         mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
         mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION);
         mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
+        setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS));
         mSectionsManager = Dependency.get(NotificationSectionsManager.class);
         mScreenOffAnimationController =
                 Dependency.get(ScreenOffAnimationController.class);
@@ -622,6 +650,9 @@
         mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
         mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        if (mAnimatedInsets) {
+            setWindowInsetsAnimationCallback(mInsetsCallback);
+        }
     }
 
     /**
@@ -690,6 +721,11 @@
     }
 
     @VisibleForTesting
+    void setAnimatedInsetsEnabled(boolean enabled) {
+        mAnimatedInsets = enabled;
+    }
+
+    @VisibleForTesting
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateFooter() {
         if (mFooterView == null) {
@@ -1781,7 +1817,11 @@
             return;
         }
         mForcedScroll = v;
-        scrollTo(v);
+        if (mAnimatedInsets) {
+            updateForcedScroll();
+        } else {
+            scrollTo(v);
+        }
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -1813,26 +1853,46 @@
                 + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
     }
 
+    private void updateBottomInset(WindowInsets windowInsets) {
+        mBottomInset = windowInsets.getInsets(WindowInsets.Type.ime()).bottom;
+
+        if (mForcedScroll != null) {
+            updateForcedScroll();
+        }
+
+        int range = getScrollRange();
+        if (mOwnScrollY > range) {
+            setOwnScrollY(range);
+        }
+    }
+
     @Override
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom;
+        if (!mAnimatedInsets) {
+            mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom;
+        }
         mWaterfallTopInset = 0;
         final DisplayCutout cutout = insets.getDisplayCutout();
         if (cutout != null) {
             mWaterfallTopInset = cutout.getWaterfallInsets().top;
         }
-
-        int range = getScrollRange();
-        if (mOwnScrollY > range) {
-            // HACK: We're repeatedly getting staggered insets here while the IME is
-            // animating away. To work around that we'll wait until things have settled.
-            removeCallbacks(mReclamp);
-            postDelayed(mReclamp, 50);
-        } else if (mForcedScroll != null) {
-            // The scroll was requested before we got the actual inset - in case we need
-            // to scroll up some more do so now.
-            scrollTo(mForcedScroll);
+        if (mAnimatedInsets && !mIsInsetAnimationRunning) {
+            // update bottom inset e.g. after rotation
+            updateBottomInset(insets);
+        }
+        if (!mAnimatedInsets) {
+            int range = getScrollRange();
+            if (mOwnScrollY > range) {
+                // HACK: We're repeatedly getting staggered insets here while the IME is
+                // animating away. To work around that we'll wait until things have settled.
+                removeCallbacks(mReclamp);
+                postDelayed(mReclamp, 50);
+            } else if (mForcedScroll != null) {
+                // The scroll was requested before we got the actual inset - in case we need
+                // to scroll up some more do so now.
+                scrollTo(mForcedScroll);
+            }
         }
         return insets;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index c72eb05..39b5b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -40,6 +40,7 @@
 import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
 import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
@@ -288,10 +289,14 @@
      * @param mobileContext possibly mcc/mnc overridden mobile context
      * @param subId the subscriptionId for this mobile view
      */
-    public void addModernMobileView(Context mobileContext, int subId) {
+    public void addModernMobileView(
+            Context mobileContext,
+            MobileViewLogger mobileViewLogger,
+            int subId) {
         Log.d(TAG, "addModernMobileView (subId=" + subId + ")");
         ModernStatusBarMobileView view = ModernStatusBarMobileView.constructAndBind(
                 mobileContext,
+                mobileViewLogger,
                 "mobile",
                 mMobileIconsViewModel.viewModelForSub(subId, mLocation)
         );
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 11863627..04cc8ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -569,7 +569,10 @@
             mGroup.addView(view, index, onCreateLayoutParams());
 
             if (mIsInDemoMode) {
-                mDemoStatusIcons.addModernMobileView(mContext, subId);
+                mDemoStatusIcons.addModernMobileView(
+                        mContext,
+                        mMobileIconsViewModel.getLogger(),
+                        subId);
             }
 
             return view;
@@ -601,6 +604,7 @@
             return ModernStatusBarMobileView
                     .constructAndBind(
                             mobileContext,
+                            mMobileIconsViewModel.getLogger(),
                             slot,
                             mMobileIconsViewModel.viewModelForSub(subId, mLocation)
                         );
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index f6c0da8..833cb93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -79,6 +79,18 @@
     private @IconType int mType = TYPE_ICON;
     private int mTag = 0;
 
+    /** Returns a human-readable string representing the given type. */
+    public static String getTypeString(@IconType int type) {
+        switch(type) {
+            case TYPE_ICON: return "ICON";
+            case TYPE_WIFI: return "WIFI_OLD";
+            case TYPE_MOBILE: return "MOBILE_OLD";
+            case TYPE_MOBILE_NEW: return "MOBILE_NEW";
+            case TYPE_WIFI_NEW: return "WIFI_NEW";
+            default: return "UNKNOWN";
+        }
+    }
+
     private StatusBarIconHolder() {
     }
 
@@ -230,4 +242,11 @@
     public int getTag() {
         return mTag;
     }
+
+    @Override
+    public String toString() {
+        return "StatusBarIconHolder(type=" + getTypeString(mType)
+                + " tag=" + getTag()
+                + " visible=" + isVisible() + ")";
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index 8800b05..565481a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -27,6 +27,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /** A class holding the list of all the system icons that could be shown in the status bar. */
 public class StatusBarIconList {
@@ -302,7 +303,7 @@
 
         @Override
         public String toString() {
-            return String.format("(%s) %s", mName, subSlotsString());
+            return String.format("(%s) holder=%s %s", mName, mHolder, subSlotsString());
         }
 
         private String subSlotsString() {
@@ -310,7 +311,10 @@
                 return "";
             }
 
-            return "" + mSubSlots.size() + " subSlots";
+            return "| " + mSubSlots.size() + " subSlots: "
+                    + mSubSlots.stream()
+                    .map(StatusBarIconHolder::toString)
+                    .collect(Collectors.joining("|"));
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
index 79c0984..d30d0e25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController.StatusBarViewsCenterProvider
 import com.android.systemui.unfold.SysUIUnfoldScope
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.util.CurrentActivityTypeProvider
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
 import javax.inject.Inject
 import kotlin.math.max
@@ -29,9 +30,13 @@
 @SysUIUnfoldScope
 class StatusBarMoveFromCenterAnimationController @Inject constructor(
     private val progressProvider: ScopedUnfoldTransitionProgressProvider,
+    private val currentActivityTypeProvider: CurrentActivityTypeProvider,
     windowManager: WindowManager
 ) {
 
+    // Whether we're on home activity. Updated only when the animation starts.
+    private var isOnHomeActivity: Boolean? = null
+
     private val transitionListener = TransitionListener()
     private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(
         windowManager,
@@ -60,6 +65,10 @@
     }
 
     private inner class TransitionListener : TransitionProgressListener {
+        override fun onTransitionStarted() {
+            isOnHomeActivity = currentActivityTypeProvider.isHomeActivity
+        }
+
         override fun onTransitionProgress(progress: Float) {
             moveFromCenterAnimator.onTransitionProgress(progress)
         }
@@ -68,11 +77,23 @@
             // Reset translations when transition is stopped/cancelled
             // (e.g. the transition could be cancelled mid-way when rotating the screen)
             moveFromCenterAnimator.onTransitionProgress(1f)
+            isOnHomeActivity = null
         }
     }
 
-    private class StatusBarIconsAlphaProvider : AlphaProvider {
+
+    /**
+     * In certain cases, an alpha is applied based on the progress.
+     *
+     * This mainly happens to hide the statusbar during the unfold animation while on apps, as the
+     * bounds of the app "collapse" to the center, but the statusbar doesn't.
+     * While on launcher, this alpha is not applied.
+     */
+    private inner class StatusBarIconsAlphaProvider : AlphaProvider {
         override fun getAlpha(progress: Float): Float {
+            if (isOnHomeActivity == true) {
+                return 1.0f
+            }
             return max(
                 0f,
                 (progress - ICONS_START_APPEARING_PROGRESS) / (1 - ICONS_START_APPEARING_PROGRESS)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileViewLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileViewLog.kt
new file mode 100644
index 0000000..e594a8a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileViewLog.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Logs for changes with the new mobile views. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class MobileViewLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 4464751..adfea80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -148,5 +148,19 @@
         fun provideMobileInputLogBuffer(factory: LogBufferFactory): LogBuffer {
             return factory.create("MobileInputLog", 100)
         }
+
+        @Provides
+        @SysUISingleton
+        @MobileViewLog
+        fun provideMobileViewLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("MobileViewLog", 100)
+        }
+
+        @Provides
+        @SysUISingleton
+        @VerboseMobileViewLog
+        fun provideVerboseMobileViewLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("VerboseMobileViewLog", 100)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseMobileViewLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseMobileViewLog.kt
new file mode 100644
index 0000000..b987898
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseMobileViewLog.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Logs for **verbose** changes with the new mobile views. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class VerboseMobileViewLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 3cbd2b7..159f689 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.pipeline.mobile.shared
+package com.android.systemui.statusbar.pipeline.mobile.data
 
 import android.net.Network
 import android.net.NetworkCapabilities
@@ -133,24 +133,6 @@
         )
     }
 
-    fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
-        buffer.log(
-            TAG,
-            LogLevel.INFO,
-            { str1 = subs.toString() },
-            { "Sub IDs in MobileUiAdapter updated internally: $str1" },
-        )
-    }
-
-    fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) {
-        buffer.log(
-            TAG,
-            LogLevel.INFO,
-            { str1 = subs.toString() },
-            { "Sub IDs in MobileUiAdapter being sent to icon controller: $str1" },
-        )
-    }
-
     fun logCarrierConfigChanged(subId: Int) {
         buffer.log(
             TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
index bb3b9b2..efdce06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
@@ -30,8 +30,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 96b96f1..e182bc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
@@ -47,7 +48,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
 import javax.inject.Inject
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 53a208c..f97e41c 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
@@ -45,11 +45,11 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index da63ab1..075e6ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -55,17 +54,14 @@
     interactor: MobileIconsInteractor,
     private val iconController: StatusBarIconController,
     private val iconsViewModelFactory: MobileIconsViewModel.Factory,
-    private val logger: MobileInputLogger,
+    private val logger: MobileViewLogger,
     @Application private val scope: CoroutineScope,
     private val statusBarPipelineFlags: StatusBarPipelineFlags,
 ) : CoreStartable {
     private val mobileSubIds: Flow<List<Int>> =
-        interactor.filteredSubscriptions
-            .mapLatest { subscriptions ->
-                subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
-            }
-            .distinctUntilChanged()
-            .onEach { logger.logUiAdapterSubIdsUpdated(it) }
+        interactor.filteredSubscriptions.mapLatest { subscriptions ->
+            subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
+        }
 
     /**
      * We expose the list of tracked subscriptions as a flow of a list of ints, where each int is
@@ -75,7 +71,10 @@
      * NOTE: this should go away as the view presenter learns more about this data pipeline
      */
     private val mobileSubIdsState: StateFlow<List<Int>> =
-        mobileSubIds.stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+        mobileSubIds
+            .distinctUntilChanged()
+            .onEach { logger.logUiAdapterSubIdsUpdated(it) }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
 
     /** In order to keep the logs tame, we will reuse the same top-level mobile icons view model */
     val mobileIconsViewModel = iconsViewModelFactory.create(mobileSubIdsState)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
new file mode 100644
index 0000000..90dff23
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.statusbar.pipeline.mobile.ui
+
+import android.view.View
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.MobileViewLog
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Logs for changes with the new mobile views. */
+@SysUISingleton
+class MobileViewLogger
+@Inject
+constructor(
+    @MobileViewLog private val buffer: LogBuffer,
+    dumpManager: DumpManager,
+) : Dumpable {
+    init {
+        dumpManager.registerNormalDumpable(this)
+    }
+
+    private val collectionStatuses = mutableMapOf<String, Boolean>()
+
+    fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = subs.toString() },
+            { "Sub IDs in MobileUiAdapter updated internally: $str1" },
+        )
+    }
+
+    fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = subs.toString() },
+            { "Sub IDs in MobileUiAdapter being sent to icon controller: $str1" },
+        )
+    }
+
+    fun logNewViewBinding(view: View, viewModel: LocationBasedMobileViewModel) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                str1 = view.getIdForLogging()
+                str2 = viewModel.getIdForLogging()
+                str3 = viewModel.locationName
+            },
+            { "New view binding. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
+        )
+    }
+
+    fun logCollectionStarted(view: View, viewModel: LocationBasedMobileViewModel) {
+        collectionStatuses[view.getIdForLogging()] = true
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                str1 = view.getIdForLogging()
+                str2 = viewModel.getIdForLogging()
+                str3 = viewModel.locationName
+            },
+            { "Collection started. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
+        )
+    }
+
+    fun logCollectionStopped(view: View, viewModel: LocationBasedMobileViewModel) {
+        collectionStatuses[view.getIdForLogging()] = false
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                str1 = view.getIdForLogging()
+                str2 = viewModel.getIdForLogging()
+                str3 = viewModel.locationName
+            },
+            { "Collection stopped. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
+        )
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("Collection statuses per view:---")
+        collectionStatuses.forEach { viewId, isCollecting ->
+            pw.println("viewId=$viewId, isCollecting=$isCollecting")
+        }
+    }
+
+    companion object {
+        fun Any.getIdForLogging(): String {
+            // The identityHashCode is guaranteed to be constant for the lifetime of the object.
+            return Integer.toHexString(System.identityHashCode(this))
+        }
+    }
+}
+
+private const val TAG = "MobileViewLogger"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
new file mode 100644
index 0000000..f67bc8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.statusbar.pipeline.mobile.ui
+
+import android.view.View
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.VerboseMobileViewLog
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
+import javax.inject.Inject
+
+/**
+ * Logs for **verbose** changes with the new mobile views.
+ *
+ * This is a hopefully temporary log until we resolve some open bugs (b/267236367, b/269565345,
+ * b/270300839).
+ */
+@SysUISingleton
+class VerboseMobileViewLogger
+@Inject
+constructor(
+    @VerboseMobileViewLog private val buffer: LogBuffer,
+) {
+    fun logBinderReceivedSignalIcon(parentView: View, subId: Int, icon: SignalIconModel) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                str1 = parentView.getIdForLogging()
+                int1 = subId
+                int2 = icon.level
+                bool1 = icon.showExclamationMark
+            },
+            {
+                "Binder[subId=$int1, viewId=$str1] received new signal icon: " +
+                    "level=$int2 showExclamation=$bool1"
+            },
+        )
+    }
+
+    fun logBinderReceivedNetworkTypeIcon(parentView: View, subId: Int, icon: Icon.Resource?) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                str1 = parentView.getIdForLogging()
+                int1 = subId
+                bool1 = icon != null
+                int2 = icon?.res ?: -1
+            },
+            {
+                "Binder[subId=$int1, viewId=$str1] received new network type icon: " +
+                    if (bool1) "resId=$int2" else "null"
+            },
+        )
+    }
+}
+
+private const val TAG = "VerboseMobileViewLogger"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index db585e6..5b7d45b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -36,8 +36,10 @@
 import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
 import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.launch
@@ -48,6 +50,7 @@
     fun bind(
         view: ViewGroup,
         viewModel: LocationBasedMobileViewModel,
+        logger: MobileViewLogger,
     ): ModernStatusBarViewBinding {
         val mobileGroupView = view.requireViewById<ViewGroup>(R.id.mobile_group)
         val activityContainer = view.requireViewById<View>(R.id.inout_container)
@@ -70,8 +73,13 @@
         val iconTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
         val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
 
+        var isCollecting: Boolean = false
+
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
+                logger.logCollectionStarted(view, viewModel)
+                isCollecting = true
+
                 launch {
                     visibilityState.collect { state ->
                         when (state) {
@@ -96,6 +104,11 @@
                 // Set the icon for the triangle
                 launch {
                     viewModel.icon.distinctUntilChanged().collect { icon ->
+                        viewModel.verboseLogger?.logBinderReceivedSignalIcon(
+                            view,
+                            viewModel.subscriptionId,
+                            icon,
+                        )
                         mobileDrawable.level =
                             SignalDrawable.getState(
                                 icon.level,
@@ -114,6 +127,11 @@
                 // Set the network type icon
                 launch {
                     viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId ->
+                        viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon(
+                            view,
+                            viewModel.subscriptionId,
+                            dataTypeId,
+                        )
                         dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) }
                         networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE
                     }
@@ -150,6 +168,13 @@
                 }
 
                 launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } }
+
+                try {
+                    awaitCancellation()
+                } finally {
+                    isCollecting = false
+                    logger.logCollectionStopped(view, viewModel)
+                }
             }
         }
 
@@ -175,6 +200,10 @@
                 }
                 decorTint.value = newTint
             }
+
+            override fun isCollecting(): Boolean {
+                return isCollecting
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
index ed9a188..4144293d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
@@ -20,6 +20,8 @@
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView.getVisibleStateString
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
 import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView
@@ -31,6 +33,15 @@
 
     var subId: Int = -1
 
+    override fun toString(): String {
+        return "ModernStatusBarMobileView(" +
+            "slot='$slot', " +
+            "subId=$subId, " +
+            "isCollecting=${binding.isCollecting()}, " +
+            "visibleState=${getVisibleStateString(visibleState)}); " +
+            "viewString=${super.toString()}"
+    }
+
     companion object {
 
         /**
@@ -40,6 +51,7 @@
         @JvmStatic
         fun constructAndBind(
             context: Context,
+            logger: MobileViewLogger,
             slot: String,
             viewModel: LocationBasedMobileViewModel,
         ): ModernStatusBarMobileView {
@@ -48,7 +60,8 @@
                     as ModernStatusBarMobileView)
                 .also {
                     it.subId = viewModel.subscriptionId
-                    it.initView(slot) { MobileIconBinder.bind(it, viewModel) }
+                    it.initView(slot) { MobileIconBinder.bind(it, viewModel, logger) }
+                    logger.logNewViewBinding(it, viewModel)
                 }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
index 8e103f7..f775940 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
@@ -19,6 +19,7 @@
 import android.graphics.Color
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
 
 /**
  * A view model for an individual mobile icon that embeds the notion of a [StatusBarLocation]. This
@@ -26,11 +27,15 @@
  *
  * @param commonImpl for convenience, this class wraps a base interface that can provides all of the
  *   common implementations between locations. See [MobileIconViewModel]
+ * @property locationName the name of the location of this VM, used for logging.
+ * @property verboseLogger an optional logger to log extremely verbose view updates.
  */
 abstract class LocationBasedMobileViewModel(
     val commonImpl: MobileIconViewModelCommon,
     statusBarPipelineFlags: StatusBarPipelineFlags,
     debugTint: Int,
+    val locationName: String,
+    val verboseLogger: VerboseMobileViewLogger?,
 ) : MobileIconViewModelCommon by commonImpl {
     val useDebugColoring: Boolean = statusBarPipelineFlags.useDebugColoring()
 
@@ -45,11 +50,16 @@
         fun viewModelForLocation(
             commonImpl: MobileIconViewModelCommon,
             statusBarPipelineFlags: StatusBarPipelineFlags,
+            verboseMobileViewLogger: VerboseMobileViewLogger,
             loc: StatusBarLocation,
         ): LocationBasedMobileViewModel =
             when (loc) {
                 StatusBarLocation.HOME ->
-                    HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags)
+                    HomeMobileIconViewModel(
+                        commonImpl,
+                        statusBarPipelineFlags,
+                        verboseMobileViewLogger,
+                    )
                 StatusBarLocation.KEYGUARD ->
                     KeyguardMobileIconViewModel(commonImpl, statusBarPipelineFlags)
                 StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
@@ -60,20 +70,41 @@
 class HomeMobileIconViewModel(
     commonImpl: MobileIconViewModelCommon,
     statusBarPipelineFlags: StatusBarPipelineFlags,
+    verboseMobileViewLogger: VerboseMobileViewLogger,
 ) :
     MobileIconViewModelCommon,
-    LocationBasedMobileViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.CYAN)
+    LocationBasedMobileViewModel(
+        commonImpl,
+        statusBarPipelineFlags,
+        debugTint = Color.CYAN,
+        locationName = "Home",
+        verboseMobileViewLogger,
+    )
 
 class QsMobileIconViewModel(
     commonImpl: MobileIconViewModelCommon,
     statusBarPipelineFlags: StatusBarPipelineFlags,
 ) :
     MobileIconViewModelCommon,
-    LocationBasedMobileViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.GREEN)
+    LocationBasedMobileViewModel(
+        commonImpl,
+        statusBarPipelineFlags,
+        debugTint = Color.GREEN,
+        locationName = "QS",
+        // Only do verbose logging for the Home location.
+        verboseLogger = null,
+    )
 
 class KeyguardMobileIconViewModel(
     commonImpl: MobileIconViewModelCommon,
     statusBarPipelineFlags: StatusBarPipelineFlags,
 ) :
     MobileIconViewModelCommon,
-    LocationBasedMobileViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.MAGENTA)
+    LocationBasedMobileViewModel(
+        commonImpl,
+        statusBarPipelineFlags,
+        debugTint = Color.MAGENTA,
+        locationName = "Keyguard",
+        // Only do verbose logging for the Home location.
+        verboseLogger = null,
+    )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 0496278..dbb534b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -49,7 +49,7 @@
     val contentDescription: Flow<ContentDescription>
     val roaming: Flow<Boolean>
     /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
-    val networkTypeIcon: Flow<Icon?>
+    val networkTypeIcon: Flow<Icon.Resource?>
     val activityInVisible: Flow<Boolean>
     val activityOutVisible: Flow<Boolean>
     val activityContainerVisible: Flow<Boolean>
@@ -161,7 +161,7 @@
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
-    override val networkTypeIcon: Flow<Icon?> =
+    override val networkTypeIcon: Flow<Icon.Resource?> =
         combine(
                 iconInteractor.networkTypeIconGroup,
                 showNetworkTypeIcon,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 8cb52af..2b90065 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -23,6 +23,8 @@
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import javax.inject.Inject
@@ -39,6 +41,8 @@
 @Inject
 constructor(
     val subscriptionIdsFlow: StateFlow<List<Int>>,
+    val logger: MobileViewLogger,
+    private val verboseLogger: VerboseMobileViewLogger,
     private val interactor: MobileIconsInteractor,
     private val airplaneModeInteractor: AirplaneModeInteractor,
     private val constants: ConnectivityConstants,
@@ -66,6 +70,7 @@
         return LocationBasedMobileViewModel.viewModelForLocation(
             common,
             statusBarPipelineFlags,
+            verboseLogger,
             location,
         )
     }
@@ -79,6 +84,8 @@
     class Factory
     @Inject
     constructor(
+        private val logger: MobileViewLogger,
+        private val verboseLogger: VerboseMobileViewLogger,
         private val interactor: MobileIconsInteractor,
         private val airplaneModeInteractor: AirplaneModeInteractor,
         private val constants: ConnectivityConstants,
@@ -88,6 +95,8 @@
         fun create(subscriptionIdsFlow: StateFlow<List<Int>>): MobileIconsViewModel {
             return MobileIconsViewModel(
                 subscriptionIdsFlow,
+                logger,
+                verboseLogger,
                 interactor,
                 airplaneModeInteractor,
                 constants,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
index f67876b..81f8683 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
@@ -37,4 +37,7 @@
 
     /** Notifies that the decor tint has been updated (used only for the dot). */
     fun onDecorTintChanged(newTint: Int)
+
+    /** Returns true if the binding between the view and view-model is currently collecting. */
+    fun isCollecting(): Boolean
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
index b1e2812..1a13404 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
@@ -36,7 +36,7 @@
     BaseStatusBarFrameLayout(context, attrs) {
 
     private lateinit var slot: String
-    private lateinit var binding: ModernStatusBarViewBinding
+    internal lateinit var binding: ModernStatusBarViewBinding
 
     @StatusBarIconView.VisibleState
     private var iconVisibleState: Int = STATE_HIDDEN
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 2aff12c..9e8c814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
 import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -74,8 +75,12 @@
         val iconTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
         val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
 
+        var isCollecting: Boolean = false
+
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
+                isCollecting = true
+
                 launch {
                     visibilityState.collect { visibilityState ->
                         groupView.isVisible = visibilityState == STATE_ICON
@@ -127,6 +132,12 @@
                         airplaneSpacer.isVisible = visible
                     }
                 }
+
+                try {
+                    awaitCancellation()
+                } finally {
+                    isCollecting = false
+                }
             }
         }
 
@@ -152,6 +163,10 @@
                 }
                 decorTint.value = newTint
             }
+
+            override fun isCollecting(): Boolean {
+                return isCollecting
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
index 7a73486..f23e102 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
@@ -21,6 +21,7 @@
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView
 import com.android.systemui.statusbar.pipeline.wifi.ui.binder.WifiViewBinder
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
@@ -33,6 +34,15 @@
     context: Context,
     attrs: AttributeSet?,
 ) : ModernStatusBarView(context, attrs) {
+
+    override fun toString(): String {
+        return "ModernStatusBarWifiView(" +
+            "slot='$slot', " +
+            "isCollecting=${binding.isCollecting()}, " +
+            "visibleState=${StatusBarIconView.getVisibleStateString(visibleState)}); " +
+            "viewString=${super.toString()}"
+    }
+
     companion object {
         /**
          * Inflates a new instance of [ModernStatusBarWifiView], binds it to a view model, and
@@ -45,12 +55,9 @@
             slot: String,
             wifiViewModel: LocationBasedWifiViewModel,
         ): ModernStatusBarWifiView {
-            return (
-                LayoutInflater.from(context).inflate(R.layout.new_status_bar_wifi_group, null)
-                    as ModernStatusBarWifiView
-                ).also {
-                    it.initView(slot) { WifiViewBinder.bind(it, wifiViewModel) }
-                }
+            return (LayoutInflater.from(context).inflate(R.layout.new_status_bar_wifi_group, null)
+                    as ModernStatusBarWifiView)
+                .also { it.initView(slot) { WifiViewBinder.bind(it, wifiViewModel) } }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 7acdaff..01fabcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -22,13 +22,18 @@
 import android.annotation.Nullable;
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.Trace;
-import android.util.Log;
+import android.util.IndentingPrintWriter;
+
+import androidx.annotation.NonNull;
 
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
 
+import java.io.PrintWriter;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -39,19 +44,19 @@
  */
 @SysUISingleton
 public final class DeviceStateRotationLockSettingController
-        implements Listenable, RotationLockController.RotationLockControllerCallback {
-
-    private static final String TAG = "DSRotateLockSettingCon";
+        implements Listenable, RotationLockController.RotationLockControllerCallback, Dumpable {
 
     private final RotationPolicyWrapper mRotationPolicyWrapper;
     private final DeviceStateManager mDeviceStateManager;
     private final Executor mMainExecutor;
     private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
+    private final DeviceStateRotationLockSettingControllerLogger mLogger;
 
     // On registration for DeviceStateCallback, we will receive a callback with the current state
     // and this will be initialized.
     private int mDeviceState = -1;
-    @Nullable private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+    @Nullable
+    private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
     private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
             mDeviceStateRotationLockSettingsListener;
 
@@ -60,21 +65,27 @@
             RotationPolicyWrapper rotationPolicyWrapper,
             DeviceStateManager deviceStateManager,
             @Main Executor executor,
-            DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager) {
+            DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager,
+            DeviceStateRotationLockSettingControllerLogger logger,
+            DumpManager dumpManager) {
         mRotationPolicyWrapper = rotationPolicyWrapper;
         mDeviceStateManager = deviceStateManager;
         mMainExecutor = executor;
         mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
+        mLogger = logger;
+        dumpManager.registerDumpable(this);
     }
 
     @Override
     public void setListening(boolean listening) {
+        mLogger.logListeningChange(listening);
         if (listening) {
             // Note that this is called once with the initial state of the device, even if there
             // is no user action.
             mDeviceStateCallback = this::updateDeviceState;
             mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
-            mDeviceStateRotationLockSettingsListener = () -> readPersistedSetting(mDeviceState);
+            mDeviceStateRotationLockSettingsListener = () ->
+                    readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
             mDeviceStateRotationLockSettingsManager.registerListener(
                     mDeviceStateRotationLockSettingsListener);
         } else {
@@ -89,35 +100,28 @@
     }
 
     @Override
-    public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
-        if (mDeviceState == -1) {
-            Log.wtf(TAG, "Device state was not initialized.");
+    public void onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible) {
+        int deviceState = mDeviceState;
+        boolean currentRotationLocked = mDeviceStateRotationLockSettingsManager
+                .isRotationLocked(deviceState);
+        mLogger.logRotationLockStateChanged(deviceState, newRotationLocked, currentRotationLocked);
+        if (deviceState == -1) {
             return;
         }
-
-        if (rotationLocked
-                == mDeviceStateRotationLockSettingsManager.isRotationLocked(mDeviceState)) {
-            Log.v(TAG, "Rotation lock same as the current setting, no need to update.");
+        if (newRotationLocked == currentRotationLocked) {
             return;
         }
-
-        saveNewRotationLockSetting(rotationLocked);
+        saveNewRotationLockSetting(newRotationLocked);
     }
 
     private void saveNewRotationLockSetting(boolean isRotationLocked) {
-        Log.v(
-                TAG,
-                "saveNewRotationLockSetting [state="
-                        + mDeviceState
-                        + "] [isRotationLocked="
-                        + isRotationLocked
-                        + "]");
-
-        mDeviceStateRotationLockSettingsManager.updateSetting(mDeviceState, isRotationLocked);
+        int deviceState = mDeviceState;
+        mLogger.logSaveNewRotationLockSetting(isRotationLocked, deviceState);
+        mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked);
     }
 
     private void updateDeviceState(int state) {
-        Log.v(TAG, "updateDeviceState [state=" + state + "]");
+        mLogger.logUpdateDeviceState(mDeviceState, state);
         if (Trace.isEnabled()) {
             Trace.traceBegin(
                     Trace.TRACE_TAG_APP, "updateDeviceState [state=" + state + "]");
@@ -127,22 +131,26 @@
                 return;
             }
 
-            readPersistedSetting(state);
+            readPersistedSetting("updateDeviceState", state);
         } finally {
             Trace.endSection();
         }
     }
 
-    private void readPersistedSetting(int state) {
+    private void readPersistedSetting(String caller, int state) {
         int rotationLockSetting =
                 mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
+        boolean shouldBeLocked = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+        boolean isLocked = mRotationPolicyWrapper.isRotationLocked();
+
+        mLogger.readPersistedSetting(caller, state, rotationLockSetting, shouldBeLocked, isLocked);
+
         if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
             // This should not happen. Device states that have an ignored setting, should also
             // specify a fallback device state which is not ignored.
             // We won't handle this device state. The same rotation lock setting as before should
             // apply and any changes to the rotation lock setting will be written for the previous
             // valid device state.
-            Log.w(TAG, "Missing fallback. Ignoring new device state: " + state);
             return;
         }
 
@@ -150,9 +158,18 @@
         mDeviceState = state;
 
         // Update the rotation policy, if needed, for this new device state
-        boolean newRotationLockSetting = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
-        if (newRotationLockSetting != mRotationPolicyWrapper.isRotationLocked()) {
-            mRotationPolicyWrapper.setRotationLock(newRotationLockSetting);
+        if (shouldBeLocked != isLocked) {
+            mRotationPolicyWrapper.setRotationLock(shouldBeLocked);
         }
     }
+
+    @Override
+    public void dump(@NonNull PrintWriter printWriter, @NonNull String[] args) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter);
+        mDeviceStateRotationLockSettingsManager.dump(pw);
+        pw.println("DeviceStateRotationLockSettingController");
+        pw.increaseIndent();
+        pw.println("mDeviceState: " + mDeviceState);
+        pw.decreaseIndent();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
new file mode 100644
index 0000000..aa502bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.statusbar.policy
+
+import android.content.Context
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
+import com.android.internal.R
+import com.android.systemui.log.dagger.DeviceStateAutoRotationLog
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel.VERBOSE
+import javax.inject.Inject
+
+class DeviceStateRotationLockSettingControllerLogger
+@Inject
+constructor(@DeviceStateAutoRotationLog private val logBuffer: LogBuffer, context: Context) {
+
+    private val foldedStates = context.resources.getIntArray(R.array.config_foldedDeviceStates)
+    private val halfFoldedStates =
+        context.resources.getIntArray(R.array.config_halfFoldedDeviceStates)
+    private val unfoldedStates = context.resources.getIntArray(R.array.config_openDeviceStates)
+
+    fun logListeningChange(listening: Boolean) {
+        logBuffer.log(TAG, VERBOSE, { bool1 = listening }, { "setListening: $bool1" })
+    }
+
+    fun logRotationLockStateChanged(
+        state: Int,
+        newRotationLocked: Boolean,
+        currentRotationLocked: Boolean
+    ) {
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                int1 = state
+                bool1 = newRotationLocked
+                bool2 = currentRotationLocked
+            },
+            {
+                "onRotationLockStateChanged: " +
+                    "state=$int1 [${int1.toDevicePostureString()}], " +
+                    "newRotationLocked=$bool1, " +
+                    "currentRotationLocked=$bool2"
+            }
+        )
+    }
+
+    fun logSaveNewRotationLockSetting(isRotationLocked: Boolean, state: Int) {
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                bool1 = isRotationLocked
+                int1 = state
+            },
+            { "saveNewRotationLockSetting: isRotationLocked=$bool1, state=$int1" }
+        )
+    }
+
+    fun logUpdateDeviceState(currentState: Int, newState: Int) {
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                int1 = currentState
+                int2 = newState
+            },
+            {
+                "updateDeviceState: " +
+                    "current=$int1 [${int1.toDevicePostureString()}], " +
+                    "new=$int2 [${int2.toDevicePostureString()}]"
+            }
+        )
+    }
+
+    fun readPersistedSetting(
+        caller: String,
+        state: Int,
+        rotationLockSetting: Int,
+        shouldBeLocked: Boolean,
+        isLocked: Boolean
+    ) {
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = caller
+                int1 = state
+                int2 = rotationLockSetting
+                bool1 = shouldBeLocked
+                bool2 = isLocked
+            },
+            {
+                "readPersistedSetting: " +
+                    "caller=$str1, " +
+                    "state=$int1 [${int1.toDevicePostureString()}], " +
+                    "rotationLockSettingForState: ${int2.toRotationLockSettingString()}, " +
+                    "shouldBeLocked=$bool1, " +
+                    "isLocked=$bool2"
+            }
+        )
+    }
+
+    private fun Int.toDevicePostureString(): String {
+        return when (this) {
+            in foldedStates -> "Folded"
+            in unfoldedStates -> "Unfolded"
+            in halfFoldedStates -> "Half-Folded"
+            -1 -> "Uninitialized"
+            else -> "Unknown"
+        }
+    }
+}
+
+private fun Int.toRotationLockSettingString(): String {
+    return when (this) {
+        DEVICE_STATE_ROTATION_LOCK_IGNORED -> "IGNORED"
+        DEVICE_STATE_ROTATION_LOCK_LOCKED -> "LOCKED"
+        DEVICE_STATE_ROTATION_LOCK_UNLOCKED -> "UNLOCKED"
+        else -> "Unknown"
+    }
+}
+
+private const val TAG = "DSRotateLockSettingCon"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java
index 3503902..9d16185 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java
@@ -45,8 +45,8 @@
 public class BroadcastDialogTest extends SysuiTestCase {
 
     private static final String CURRENT_BROADCAST_APP = "Music";
-    private static final String SWITCH_APP = "Files by Google";
-    private static final String TEST_PACKAGE = "com.google.android.apps.nbu.files";
+    private static final String SWITCH_APP = "System UI";
+    private static final String TEST_PACKAGE = "com.android.systemui";
     private BroadcastDialog mBroadcastDialog;
     private View mDialogView;
     private TextView mTitle;
@@ -59,6 +59,7 @@
         MockitoAnnotations.initMocks(this);
         mBroadcastDialog = new BroadcastDialog(mContext, mock(MediaOutputDialogFactory.class),
                 CURRENT_BROADCAST_APP, TEST_PACKAGE, mock(UiEventLogger.class));
+
         mBroadcastDialog.show();
         mDialogView = mBroadcastDialog.mDialogView;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index c2fb904..ffd75fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.app.RemoteAction;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -101,6 +102,9 @@
     private ArgumentCaptor<ClipboardOverlayView.ClipboardOverlayCallbacks> mOverlayCallbacksCaptor;
     private ClipboardOverlayView.ClipboardOverlayCallbacks mCallbacks;
 
+    @Captor
+    private ArgumentCaptor<AnimatorListenerAdapter> mAnimatorArgumentCaptor;
+
     private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Before
@@ -478,12 +482,16 @@
         when(mClipboardOverlayWindow.getWindowInsets()).thenReturn(
                 getImeInsets(new Rect(0, 0, 0, 1)));
         mOverlayController.setClipData(mSampleClipData, "");
+        Animator mockFadeoutAnimator = Mockito.mock(Animator.class);
+        when(mClipboardOverlayView.getMinimizedFadeoutAnimation()).thenReturn(mockFadeoutAnimator);
 
         verify(mClipboardOverlayView).setMinimized(true);
         verify(mClipboardOverlayView, never()).setMinimized(false);
         verify(mClipboardOverlayView, never()).showTextPreview(any(), anyBoolean());
 
         mCallbacks.onMinimizedViewTapped();
+        verify(mockFadeoutAnimator).addListener(mAnimatorArgumentCaptor.capture());
+        mAnimatorArgumentCaptor.getValue().onAnimationEnd(mockFadeoutAnimator);
 
         verify(mClipboardOverlayView).setMinimized(false);
         verify(mClipboardOverlayView).showTextPreview("Test Item", false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
index eafe727..afd9be5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
@@ -107,4 +107,32 @@
 
         assertThat(mSeekbar.getProgress()).isEqualTo(0);
     }
+
+    @Test
+    public void setProgressStateLabels_getExpectedStateDescriptionOnInitialization() {
+        String[] stateLabels = new String[]{"1", "2", "3", "4", "5"};
+        mIconDiscreteSliderLinearLayout.setMax(stateLabels.length);
+        mIconDiscreteSliderLinearLayout.setProgress(1);
+        mIconDiscreteSliderLinearLayout.setProgressStateLabels(stateLabels);
+
+        final int currentProgress = mSeekbar.getProgress();
+        final CharSequence stateDescription = mSeekbar.getStateDescription();
+
+        assertThat(currentProgress).isEqualTo(1);
+        assertThat(stateDescription).isEqualTo(stateLabels[currentProgress]);
+    }
+
+    @Test
+    public void setProgressStateLabels_progressChanged_getExpectedStateDescription() {
+        String[] stateLabels = new String[]{"1", "2", "3", "4", "5"};
+        mIconDiscreteSliderLinearLayout.setMax(stateLabels.length);
+        mIconDiscreteSliderLinearLayout.setProgressStateLabels(stateLabels);
+        mIconDiscreteSliderLinearLayout.setProgress(1);
+
+        final int currentProgress = mSeekbar.getProgress();
+        final CharSequence stateDescription = mSeekbar.getStateDescription();
+
+        assertThat(currentProgress).isEqualTo(1);
+        assertThat(stateDescription).isEqualTo(stateLabels[currentProgress]);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 6c23254..0a94706 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -1,6 +1,8 @@
 package com.android.systemui.dreams
 
+import android.animation.Animator
 import android.animation.AnimatorSet
+import android.animation.ValueAnimator
 import android.testing.AndroidTestingRunner
 import android.view.View
 import androidx.test.filters.SmallTest
@@ -10,13 +12,16 @@
 import com.android.systemui.statusbar.BlurUtils
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertTrue
 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.anyLong
 import org.mockito.Mockito.eq
@@ -71,6 +76,19 @@
     }
 
     @Test
+    fun testExitAnimationUpdatesState() {
+        controller.startExitAnimations(animatorBuilder = { mockAnimator })
+
+        verify(stateController).setExitAnimationsRunning(true)
+
+        val captor = argumentCaptor<Animator.AnimatorListener>()
+        verify(mockAnimator).addListener(captor.capture())
+
+        captor.value.onAnimationEnd(mockAnimator)
+        verify(stateController).setExitAnimationsRunning(false)
+    }
+
+    @Test
     fun testWakeUpCallsExecutor() {
         val mockExecutor: DelayableExecutor = mock()
         val mockCallback: Runnable = mock()
@@ -87,7 +105,7 @@
     fun testWakeUpAfterStartWillCancel() {
         val mockStartAnimator: AnimatorSet = mock()
 
-        controller.startEntryAnimations(animatorBuilder = { mockStartAnimator })
+        controller.startEntryAnimations(false, animatorBuilder = { mockStartAnimator })
 
         verify(mockStartAnimator, never()).cancel()
 
@@ -100,4 +118,50 @@
         // animator.
         verify(mockStartAnimator, times(1)).cancel()
     }
+
+    @Test
+    fun testEntryAnimations_translatesUpwards() {
+        val mockStartAnimator: AnimatorSet = mock()
+
+        controller.startEntryAnimations(
+            /* downwards= */ false,
+            animatorBuilder = { mockStartAnimator }
+        )
+
+        val animatorCaptor = ArgumentCaptor.forClass(Animator::class.java)
+        verify(mockStartAnimator).playTogether(animatorCaptor.capture())
+
+        // Check if there's a ValueAnimator starting at the expected Y distance.
+        val animators: List<ValueAnimator> = animatorCaptor.allValues as List<ValueAnimator>
+        assertTrue(
+            animators.any {
+                // Call setCurrentFraction so the animated value jumps to the initial value.
+                it.setCurrentFraction(0f)
+                it.animatedValue == DREAM_IN_TRANSLATION_Y_DISTANCE.toFloat()
+            }
+        )
+    }
+
+    @Test
+    fun testEntryAnimations_translatesDownwards() {
+        val mockStartAnimator: AnimatorSet = mock()
+
+        controller.startEntryAnimations(
+            /* downwards= */ true,
+            animatorBuilder = { mockStartAnimator }
+        )
+
+        val animatorCaptor = ArgumentCaptor.forClass(Animator::class.java)
+        verify(mockStartAnimator).playTogether(animatorCaptor.capture())
+
+        // Check if there's a ValueAnimator starting at the expected Y distance.
+        val animators: List<ValueAnimator> = animatorCaptor.allValues as List<ValueAnimator>
+        assertTrue(
+            animators.any {
+                // Call setCurrentFraction so the animated value jumps to the initial value.
+                it.setCurrentFraction(0f)
+                it.animatedValue == -DREAM_IN_TRANSLATION_Y_DISTANCE.toFloat()
+            }
+        )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 6b095ff..2a72e7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -33,6 +34,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.dream.lowlight.LowLightTransitionCoordinator;
 import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dreams.complication.ComplicationHostViewController;
@@ -65,6 +67,9 @@
     DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
 
     @Mock
+    LowLightTransitionCoordinator mLowLightTransitionCoordinator;
+
+    @Mock
     DreamOverlayContainerView mDreamOverlayContainerView;
 
     @Mock
@@ -109,6 +114,7 @@
                 mComplicationHostViewController,
                 mDreamOverlayContentView,
                 mDreamOverlayStatusBarViewController,
+                mLowLightTransitionCoordinator,
                 mBlurUtils,
                 mHandler,
                 mResources,
@@ -200,7 +206,7 @@
 
         mController.onViewAttached();
 
-        verify(mAnimationsController).startEntryAnimations();
+        verify(mAnimationsController).startEntryAnimations(false);
         verify(mAnimationsController, never()).cancelAnimations();
     }
 
@@ -210,11 +216,11 @@
 
         mController.onViewAttached();
 
-        verify(mAnimationsController, never()).startEntryAnimations();
+        verify(mAnimationsController, never()).startEntryAnimations(anyBoolean());
     }
 
     @Test
-    public void testSkipEntryAnimationsWhenExitingLowLight() {
+    public void testDownwardEntryAnimationsWhenExitingLowLight() {
         ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor =
                 ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
         when(mStateController.isLowLightActive()).thenReturn(false);
@@ -230,8 +236,14 @@
         mController.onViewAttached();
 
         // Entry animations should be started then immediately ended to skip to the end.
-        verify(mAnimationsController).startEntryAnimations();
-        verify(mAnimationsController).endAnimations();
+        verify(mAnimationsController).startEntryAnimations(true);
+    }
+
+    @Test
+    public void testStartsExitAnimationsBeforeEnteringLowLight() {
+        mController.onBeforeEnterLowLight();
+
+        verify(mAnimationsController).startExitAnimations();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
index dcd8736..068852d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
@@ -22,6 +22,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
 
@@ -33,6 +35,8 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -96,6 +100,10 @@
 
     private ComplicationHostViewController mController;
 
+    private SecureSettings mSecureSettings;
+
+    private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -108,12 +116,17 @@
         when(mViewHolder.getLayoutParams()).thenReturn(mComplicationLayoutParams);
         when(mComplicationView.getParent()).thenReturn(mComplicationHostView);
 
+        mSecureSettings = new FakeSettings();
+        mSecureSettings.putFloatForUser(
+                        Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, CURRENT_USER_ID);
+
         mController = new ComplicationHostViewController(
                 mComplicationHostView,
                 mLayoutEngine,
                 mDreamOverlayStateController,
                 mLifecycleOwner,
-                mViewModel);
+                mViewModel,
+                mSecureSettings);
 
         mController.init();
     }
@@ -188,6 +201,23 @@
         verify(mComplicationView, never()).setVisibility(View.INVISIBLE);
     }
 
+    @Test
+    public void testAnimationsDisabled_ComplicationsNeverSetToInvisible() {
+        //Disable animations
+        mController.mIsAnimationEnabled = false;
+
+        final Observer<Collection<ComplicationViewModel>> observer =
+                captureComplicationViewModelsObserver();
+
+        // Add a complication before entry animations are finished.
+        final HashSet<ComplicationViewModel> complications = new HashSet<>(
+                Collections.singletonList(mComplicationViewModel));
+        observer.onChanged(complications);
+
+        // The complication view should not be set to invisible.
+        verify(mComplicationView, never()).setVisibility(View.INVISIBLE);
+    }
+
     private Observer<Collection<ComplicationViewModel>> captureComplicationViewModelsObserver() {
         verify(mComplicationViewModelLiveData).observe(eq(mLifecycleOwner),
                 mObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 7f57077..e0ca90e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -18,7 +18,6 @@
 
 import android.app.PendingIntent
 import android.content.res.ColorStateList
-import android.content.res.Configuration
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.util.MathUtils.abs
@@ -685,46 +684,6 @@
     }
 
     @Test
-    fun testOnConfigChanged_playersAreAddedBack() {
-        mediaCarouselController.pageIndicator = pageIndicator
-
-        listener.value.onMediaDataLoaded(
-            "playing local",
-            null,
-            DATA.copy(
-                active = true,
-                isPlaying = true,
-                playbackLocation = MediaData.PLAYBACK_LOCAL,
-                resumption = false
-            )
-        )
-        listener.value.onMediaDataLoaded(
-            "paused local",
-            null,
-            DATA.copy(
-                active = true,
-                isPlaying = false,
-                playbackLocation = MediaData.PLAYBACK_LOCAL,
-                resumption = false
-            )
-        )
-        runAllReady()
-
-        val playersSize = MediaPlayerData.players().size
-
-        configListener.value.onConfigChanged(Configuration())
-        runAllReady()
-
-        verify(pageIndicator).tintList =
-            ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator))
-        assertEquals(playersSize, MediaPlayerData.players().size)
-        assertEquals(
-            MediaPlayerData.getMediaPlayerIndex("playing local"),
-            mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex
-        )
-    }
-
-    @Test
     fun testOnUiModeChanged_playersAreAddedBack() {
         mediaCarouselController.pageIndicator = pageIndicator
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
index af91cdb..0fac3db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
@@ -16,9 +16,12 @@
 
 package com.android.systemui.media.controls.ui
 
+import android.content.res.Configuration
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
+import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
@@ -58,6 +61,8 @@
     @Mock private lateinit var mediaSubTitleWidgetState: WidgetState
     @Mock private lateinit var mediaContainerWidgetState: WidgetState
     @Mock private lateinit var mediaFlags: MediaFlags
+    @Mock private lateinit var expandedLayout: ConstraintSet
+    @Mock private lateinit var collapsedLayout: ConstraintSet
 
     val delta = 0.1F
 
@@ -77,6 +82,19 @@
     }
 
     @Test
+    fun testOrientationChanged_layoutsAreLoaded() {
+        mediaViewController.expandedLayout = expandedLayout
+        mediaViewController.collapsedLayout = collapsedLayout
+
+        val newConfig = Configuration()
+        newConfig.orientation = ORIENTATION_LANDSCAPE
+        configurationController.onConfigurationChanged(newConfig)
+
+        verify(expandedLayout).load(context, R.xml.media_session_expanded)
+        verify(collapsedLayout).load(context, R.xml.media_session_collapsed)
+    }
+
+    @Test
     fun testObtainViewState_applySquishFraction_toPlayerTransitionViewState_height() {
         mediaViewController.attach(player, MediaViewController.TYPE.PLAYER)
         player.measureState =
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 376b7cc..4efc30f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -20,12 +20,17 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+import android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.content.pm.PackageManager
+import android.os.UserHandle
 import android.os.UserManager
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
+import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -57,8 +62,8 @@
     @Mock lateinit var keyguardManager: KeyguardManager
     @Mock lateinit var userManager: UserManager
     @Mock lateinit var eventLogger: NoteTaskEventLogger
-    @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    private val userTracker: UserTracker = FakeUserTracker()
 
     private val noteTaskInfo = NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
 
@@ -81,16 +86,14 @@
     private fun createNoteTaskController(
         isEnabled: Boolean = true,
         bubbles: Bubbles? = this.bubbles,
-        keyguardManager: KeyguardManager? = this.keyguardManager,
-        userManager: UserManager? = this.userManager,
     ): NoteTaskController =
         NoteTaskController(
             context = context,
             resolver = resolver,
             eventLogger = eventLogger,
             optionalBubbles = Optional.ofNullable(bubbles),
-            optionalUserManager = Optional.ofNullable(userManager),
-            optionalKeyguardManager = Optional.ofNullable(keyguardManager),
+            userManager = userManager,
+            keyguardManager = keyguardManager,
             isEnabled = isEnabled,
             devicePolicyManager = devicePolicyManager,
             userTracker = userTracker,
@@ -225,13 +228,19 @@
             )
 
         val intentCaptor = argumentCaptor<Intent>()
-        verify(context).startActivity(capture(intentCaptor))
+        val userCaptor = argumentCaptor<UserHandle>()
+        verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
-            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
+                .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
+                .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
             assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
         }
+        assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
         verify(eventLogger).logNoteTaskOpened(expectedInfo)
         verifyZeroInteractions(bubbles)
     }
@@ -259,7 +268,7 @@
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
-            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
             assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
         }
         verifyZeroInteractions(eventLogger)
@@ -283,13 +292,22 @@
             )
 
         val intentCaptor = argumentCaptor<Intent>()
-        verify(context).startActivity(capture(intentCaptor))
+        val userCaptor = argumentCaptor<UserHandle>()
+        verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+
+        (intentCaptor.value.flags and FLAG_ACTIVITY_NEW_TASK) == FLAG_ACTIVITY_NEW_TASK
+
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
-            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
+                .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
+                .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
             assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
         }
+        assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
         verify(eventLogger).logNoteTaskOpened(expectedInfo)
         verifyZeroInteractions(bubbles)
     }
@@ -306,28 +324,6 @@
     }
 
     @Test
-    fun showNoteTask_keyguardManagerIsNull_shouldDoNothing() {
-        createNoteTaskController(keyguardManager = null)
-            .showNoteTask(
-                entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
-                isInMultiWindowMode = false,
-            )
-
-        verifyZeroInteractions(context, bubbles, eventLogger)
-    }
-
-    @Test
-    fun showNoteTask_userManagerIsNull_shouldDoNothing() {
-        createNoteTaskController(userManager = null)
-            .showNoteTask(
-                entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
-                isInMultiWindowMode = false,
-            )
-
-        verifyZeroInteractions(context, bubbles, eventLogger)
-    }
-
-    @Test
     fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
         whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(null)
 
@@ -460,7 +456,7 @@
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
-            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
             assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
         }
     }
@@ -487,7 +483,7 @@
         intentCaptor.value.let { intent ->
             assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
             assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
-            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
             assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
index 7f64f8a..0c945df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
@@ -22,6 +22,8 @@
 import android.test.suitebuilder.annotation.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -44,13 +46,14 @@
 
     @Mock lateinit var packageManager: PackageManager
     @Mock lateinit var roleManager: RoleManager
+    private val userTracker: UserTracker = FakeUserTracker()
 
     private lateinit var underTest: NoteTaskInfoResolver
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        underTest = NoteTaskInfoResolver(context, roleManager, packageManager)
+        underTest = NoteTaskInfoResolver(roleManager, packageManager, userTracker)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index d6dfc85..ac106ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -57,6 +57,7 @@
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
@@ -351,4 +352,44 @@
             .startPendingIntentDismissingKeyguard(
                 eq(pi), nullable(), nullable<ActivityLaunchAnimator.Controller>())
     }
+
+    @Test
+    fun testActiveTileListensOnceAfterCreated() {
+        `when`(tileServiceManager.isActiveTile).thenReturn(true)
+
+        val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+        tile.initialize()
+        tile.postStale()
+        testableLooper.processAllMessages()
+
+        verify(tileServiceManager).setBindRequested(true)
+        verify(tileService).onStartListening()
+    }
+
+    @Test
+    fun testActiveTileDoesntListenAfterFirstTime() {
+        `when`(tileServiceManager.isActiveTile).thenReturn(true)
+
+        val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+        tile.initialize()
+        // Make sure we have an icon in the tile because we don't have a default icon
+        // This should not be overridden by the retrieved tile that has null icon.
+        tile.qsTile.icon = mock(Icon::class.java)
+        `when`(tile.qsTile.icon.loadDrawable(any(Context::class.java)))
+                .thenReturn(mock(Drawable::class.java))
+
+        tile.postStale()
+        testableLooper.processAllMessages()
+
+        // postStale will set it to not listening after it's done
+        verify(tileService).onStopListening()
+
+        clearInvocations(tileServiceManager, tileService)
+
+        tile.setListening(Any(), true)
+        testableLooper.processAllMessages()
+
+        verify(tileServiceManager, never()).setBindRequested(true)
+        verify(tileService, never()).onStartListening()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
new file mode 100644
index 0000000..eac0e29
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
@@ -0,0 +1,237 @@
+/*
+ * 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.statusbar.notification.collection.coordinator
+
+import android.app.Notification
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.SbnBuilder
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
+import com.android.systemui.statusbar.notification.collection.render.NotifGroupController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.SystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.InjectMocks
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class GroupWhenCoordinatorTest : SysuiTestCase() {
+
+    private lateinit var beforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
+    private lateinit var afterRenderGroupListener: OnAfterRenderGroupListener
+
+    @Mock private lateinit var pipeline: NotifPipeline
+
+    @Mock private lateinit var delayableExecutor: DelayableExecutor
+
+    @Mock private lateinit var groupController: NotifGroupController
+
+    @Mock private lateinit var systemClock: SystemClock
+
+    @InjectMocks private lateinit var coordinator: GroupWhenCoordinator
+
+    @Before
+    fun setUp() {
+        initMocks(this)
+        whenever(systemClock.currentTimeMillis()).thenReturn(NOW)
+        coordinator.attach(pipeline)
+
+        beforeFinalizeFilterListener = withArgCaptor {
+            verify(pipeline).addOnBeforeFinalizeFilterListener(capture())
+        }
+        afterRenderGroupListener = withArgCaptor {
+            verify(pipeline).addOnAfterRenderGroupListener(capture())
+        }
+    }
+
+    @Test
+    fun setNotificationGroupWhen_setClosestTimeByNow_whenAllNotificationsAreBeforeNow() {
+        // GIVEN
+        val summaryEntry = buildNotificationEntry(0, NOW)
+        val childEntry1 = buildNotificationEntry(1, NOW - 10L)
+        val childEntry2 = buildNotificationEntry(2, NOW - 100L)
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(summaryEntry)
+                .setChildren(listOf(childEntry1, childEntry2))
+                .build()
+        // WHEN
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+        afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+        // THEN
+        verify(groupController).setNotificationGroupWhen(eq(NOW - 10L))
+    }
+
+    @Test
+    fun setNotificationGroupWhen_setClosestTimeByNow_whenAllNotificationsAreAfterNow() {
+        // GIVEN
+        val summaryEntry = buildNotificationEntry(0, NOW)
+        val childEntry1 = buildNotificationEntry(1, NOW + 10L)
+        val childEntry2 = buildNotificationEntry(2, NOW + 100L)
+
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(summaryEntry)
+                .setChildren(listOf(childEntry1, childEntry2))
+                .build()
+
+        // WHEN
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+        afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+        // THEN
+        verify(groupController).setNotificationGroupWhen(eq(NOW + 10L))
+    }
+
+    @Test
+    fun setNotificationGroupWhen_setClosestFutureTimeByNow_whenThereAreBothBeforeAndAfterNow() {
+        // GIVEN
+        val summaryEntry = buildNotificationEntry(0, NOW)
+        val childEntry1 = buildNotificationEntry(1, NOW + 100L)
+        val childEntry2 = buildNotificationEntry(2, NOW + 10L)
+        val childEntry3 = buildNotificationEntry(3, NOW - 100L)
+        val childEntry4 = buildNotificationEntry(4, NOW - 9L)
+
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(summaryEntry)
+                .setChildren(listOf(childEntry1, childEntry2, childEntry3, childEntry4))
+                .build()
+
+        // WHEN
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+        afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+        // THEN
+        verify(groupController).setNotificationGroupWhen(eq(NOW + 10L))
+    }
+
+    @Test
+    fun setNotificationGroupWhen_filterInvalidNotificationTimes() {
+        // GIVEN
+        val summaryEntry = buildNotificationEntry(0, NOW)
+        val childEntry1 = buildNotificationEntry(1, NOW + 100L)
+        val childEntry2 = buildNotificationEntry(2, -20000L)
+        val childEntry3 = buildNotificationEntry(4, 0)
+
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(summaryEntry)
+                .setChildren(listOf(childEntry1, childEntry2, childEntry3))
+                .build()
+
+        // WHEN
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+        afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+        // THEN
+        verify(groupController).setNotificationGroupWhen(eq(NOW + 100))
+    }
+
+    @Test
+    fun setNotificationGroupWhen_setSummaryTimeWhenAllNotificationTimesAreInvalid() {
+        // GIVEN
+        val summaryEntry = buildNotificationEntry(0, NOW)
+        val childEntry1 = buildNotificationEntry(1, 0)
+        val childEntry2 = buildNotificationEntry(2, -1)
+
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(summaryEntry)
+                .setChildren(listOf(childEntry1, childEntry2))
+                .build()
+
+        // WHEN
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+        afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+        // THEN
+        verify(groupController, never()).setNotificationGroupWhen(NOW)
+    }
+
+    @Test
+    fun setNotificationGroupWhen_schedulePipelineInvalidationWhenAnyNotificationIsInTheFuture() {
+        // GIVEN
+        val summaryEntry = buildNotificationEntry(0, NOW)
+        val childEntry1 = buildNotificationEntry(1, NOW + 1000L)
+        val childEntry2 = buildNotificationEntry(2, NOW + 2000L)
+        val childEntry3 = buildNotificationEntry(3, NOW - 100L)
+
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(summaryEntry)
+                .setChildren(listOf(childEntry1, childEntry2, childEntry3))
+                .build()
+
+        // WHEN
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+        afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+        // THEN
+        verify(delayableExecutor).executeDelayed(any(), eq(1000))
+    }
+
+    @Test
+    fun setNotificationGroupWhen_cancelPrevPipelineInvalidation() {
+        // GIVEN
+        val summaryEntry = buildNotificationEntry(0, NOW)
+        val childEntry1 = buildNotificationEntry(1, NOW + 1L)
+        val prevInvalidation = mock<Runnable>()
+        whenever(delayableExecutor.executeDelayed(any(), any())).thenReturn(prevInvalidation)
+
+        val groupEntry =
+            GroupEntryBuilder().setSummary(summaryEntry).setChildren(listOf(childEntry1)).build()
+
+        // WHEN
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+        afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+
+        // THEN
+        verify(prevInvalidation).run()
+    }
+
+    private fun buildNotificationEntry(id: Int, timeMillis: Long): NotificationEntry {
+        val notification = Notification.Builder(mContext).setWhen(timeMillis).build()
+        val sbn = SbnBuilder().setNotification(notification).build()
+        return NotificationEntryBuilder().setId(id).setSbn(sbn).build()
+    }
+
+    private companion object {
+        private const val NOW = 1000L
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index dd7143a..cbf841b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import static android.view.View.GONE;
+import static android.view.WindowInsets.Type.ime;
 
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
@@ -46,6 +47,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -54,6 +56,8 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
 import android.widget.TextView;
 
 import androidx.test.annotation.UiThreadTest;
@@ -91,6 +95,8 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.ArrayList;
+
 /**
  * Tests for {@link NotificationStackScrollLayout}.
  */
@@ -843,6 +849,19 @@
         verify(mEmptyShadeView).setFooterText(not(0));
     }
 
+    @Test
+    public void testWindowInsetAnimationProgress_updatesBottomInset() {
+        int bottomImeInset = 100;
+        mStackScrollerInternal.setAnimatedInsetsEnabled(true);
+        WindowInsets windowInsets = new WindowInsets.Builder()
+                .setInsets(ime(), Insets.of(0, 0, 0, bottomImeInset)).build();
+        ArrayList<WindowInsetsAnimation> windowInsetsAnimations = new ArrayList<>();
+        mStackScrollerInternal
+                .dispatchWindowInsetsAnimationProgress(windowInsets, windowInsetsAnimations);
+
+        assertEquals(bottomImeInset, mStackScrollerInternal.mBottomInset);
+    }
+
     private void setBarStateForTest(int state) {
         // Can't inject this through the listener or we end up on the actual implementation
         // rather than the mock because the spy just coppied the anonymous inner /shruggie.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
index 1779de7..7594c90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
@@ -8,13 +8,14 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.util.CurrentActivityTypeProvider
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -26,6 +27,9 @@
     @Mock
     private lateinit var display: Display
 
+    @Mock
+    private lateinit var currentActivityTypeProvider: CurrentActivityTypeProvider
+
     private val view: View = View(context)
     private val progressProvider = TestUnfoldTransitionProvider()
     private val scopedProvider = ScopedUnfoldTransitionProgressProvider(progressProvider)
@@ -36,9 +40,9 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        `when`(windowManager.defaultDisplay).thenReturn(display)
-        `when`(display.rotation).thenReturn(Surface.ROTATION_0)
-        `when`(display.getSize(any())).thenAnswer {
+        whenever(windowManager.defaultDisplay).thenReturn(display)
+        whenever(display.rotation).thenReturn(Surface.ROTATION_0)
+        whenever(display.getSize(any())).thenAnswer {
             val point = it.arguments[0] as Point
             point.x = 100
             point.y = 100
@@ -47,7 +51,12 @@
 
         scopedProvider.setReadyToHandleTransition(true)
 
-        controller = StatusBarMoveFromCenterAnimationController(scopedProvider, windowManager)
+        controller =
+            StatusBarMoveFromCenterAnimationController(
+                scopedProvider,
+                currentActivityTypeProvider,
+                windowManager
+            )
     }
 
     @Test
@@ -99,6 +108,31 @@
     }
 
     @Test
+    fun alpha_onLauncher_alphaDoesNotChange() {
+        whenever(currentActivityTypeProvider.isHomeActivity).thenReturn(true)
+        controller.onViewsReady(arrayOf(view))
+        progressProvider.onTransitionStarted()
+        progressProvider.onTransitionProgress(0.0f)
+        assertThat(view.alpha).isEqualTo(1.0f)
+
+        progressProvider.onTransitionProgress(1.0f)
+
+        assertThat(view.alpha).isEqualTo(1.0f)
+    }
+
+    @Test
+    fun alpha_NotOnLauncher_alphaChanges() {
+        whenever(currentActivityTypeProvider.isHomeActivity).thenReturn(false)
+        controller.onViewsReady(arrayOf(view))
+        progressProvider.onTransitionStarted()
+        assertThat(view.alpha).isEqualTo(1.0f)
+
+        progressProvider.onTransitionProgress(0.5f)
+
+        assertThat(view.alpha).isNotEqualTo(1.0f)
+    }
+
+    @Test
     fun transitionFinished_viewReAttached_noChangesToTranslation() {
         controller.onViewsReady(arrayOf(view))
         progressProvider.onTransitionProgress(0.5f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLoggerTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLoggerTest.kt
index 86529dc..35dea60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLoggerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.pipeline.mobile.shared
+package com.android.systemui.statusbar.pipeline.mobile.data
 
 import android.net.Network
 import android.net.NetworkCapabilities
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
index 0145103..dfef62e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
@@ -24,8 +24,8 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 17502f2..07c8cee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -27,13 +27,13 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoModeMobileConnectionDataSource
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.validMobileEvent
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index b2577e3..bd5a4d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -53,6 +53,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
@@ -65,7 +66,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 09b7a66..68b1cda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -37,12 +37,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
new file mode 100644
index 0000000..4aa48d6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.statusbar.pipeline.mobile.ui
+
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.KeyguardMobileIconViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class MobileViewLoggerTest : SysuiTestCase() {
+    private val buffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10)
+    private val stringWriter = StringWriter()
+    private val printWriter = PrintWriter(stringWriter)
+
+    private val underTest = MobileViewLogger(buffer, mock())
+
+    @Mock private lateinit var flags: StatusBarPipelineFlags
+    @Mock private lateinit var commonViewModel: MobileIconViewModel
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun collectionStarted_dumpHasInfo() {
+        val view = TextView(context)
+        val viewModel = QsMobileIconViewModel(commonViewModel, flags)
+
+        underTest.logCollectionStarted(view, viewModel)
+
+        val dumpString = getDumpString()
+        assertThat(dumpString).contains("${view.getIdForLogging()}, isCollecting=true")
+    }
+
+    @Test
+    fun collectionStarted_multipleViews_dumpHasInfo() {
+        val view = TextView(context)
+        val view2 = TextView(context)
+        val viewModel = QsMobileIconViewModel(commonViewModel, flags)
+        val viewModel2 = KeyguardMobileIconViewModel(commonViewModel, flags)
+
+        underTest.logCollectionStarted(view, viewModel)
+        underTest.logCollectionStarted(view2, viewModel2)
+
+        val dumpString = getDumpString()
+        assertThat(dumpString).contains("${view.getIdForLogging()}, isCollecting=true")
+        assertThat(dumpString).contains("${view2.getIdForLogging()}, isCollecting=true")
+    }
+
+    @Test
+    fun collectionStopped_dumpHasInfo() {
+        val view = TextView(context)
+        val view2 = TextView(context)
+        val viewModel = QsMobileIconViewModel(commonViewModel, flags)
+        val viewModel2 = KeyguardMobileIconViewModel(commonViewModel, flags)
+
+        underTest.logCollectionStarted(view, viewModel)
+        underTest.logCollectionStarted(view2, viewModel2)
+        underTest.logCollectionStopped(view, viewModel)
+
+        val dumpString = getDumpString()
+        assertThat(dumpString).contains("${view.getIdForLogging()}, isCollecting=false")
+        assertThat(dumpString).contains("${view2.getIdForLogging()}, isCollecting=true")
+    }
+
+    private fun getDumpString(): String {
+        underTest.dump(printWriter, args = arrayOf())
+        return stringWriter.toString()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index e68a397..7420db2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel
@@ -60,6 +61,7 @@
 
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
+    @Mock private lateinit var viewLogger: MobileViewLogger
     @Mock private lateinit var constants: ConnectivityConstants
     private lateinit var interactor: FakeMobileIconInteractor
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -94,7 +96,13 @@
 
     @Test
     fun setVisibleState_icon_iconShownDotHidden() {
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
 
         view.setVisibleState(StatusBarIconView.STATE_ICON, /* animate= */ false)
 
@@ -109,8 +117,13 @@
 
     @Test
     fun setVisibleState_dot_iconHiddenDotShown() {
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
         view.setVisibleState(StatusBarIconView.STATE_DOT, /* animate= */ false)
 
         ViewUtils.attachView(view)
@@ -124,8 +137,13 @@
 
     @Test
     fun setVisibleState_hidden_iconAndDotHidden() {
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
         view.setVisibleState(StatusBarIconView.STATE_HIDDEN, /* animate= */ false)
 
         ViewUtils.attachView(view)
@@ -142,8 +160,13 @@
         whenever(constants.hasDataCapabilities).thenReturn(false)
         createViewModel()
 
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
 
@@ -157,8 +180,13 @@
         whenever(constants.hasDataCapabilities).thenReturn(true)
         createViewModel()
 
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
 
@@ -171,8 +199,13 @@
     fun isIconVisible_notAirplaneMode_outputsTrue() {
         airplaneModeRepository.setIsAirplaneMode(false)
 
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
 
@@ -185,8 +218,13 @@
     fun isIconVisible_airplaneMode_outputsTrue() {
         airplaneModeRepository.setIsAirplaneMode(true)
 
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
 
@@ -198,7 +236,13 @@
     @Test
     fun onDarkChanged_iconHasNewColor() {
         whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
 
@@ -214,7 +258,13 @@
     @Test
     fun setStaticDrawableColor_iconHasNewColor() {
         whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
-        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+        val view =
+            ModernStatusBarMobileView.constructAndBind(
+                context,
+                viewLogger,
+                SLOT_NAME,
+                viewModel,
+            )
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index f983030..a6d9152 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
@@ -84,7 +85,7 @@
                 testScope.backgroundScope,
             )
 
-        homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags)
+        homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags, mock())
         qsIcon = QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
         keyguardIcon = KeyguardMobileIconViewModel(commonImpl, statusBarPipelineFlags)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 4628f84..ddb7f4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -24,6 +24,8 @@
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
@@ -51,6 +53,8 @@
     private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var constants: ConnectivityConstants
+    @Mock private lateinit var logger: MobileViewLogger
+    @Mock private lateinit var verboseLogger: VerboseMobileViewLogger
 
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
@@ -73,6 +77,8 @@
         underTest =
             MobileIconsViewModel(
                 subscriptionIdsFlow,
+                logger,
+                verboseLogger,
                 interactor,
                 airplaneModeInteractor,
                 constants,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
index e4c8fd0..b4039d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
@@ -164,6 +164,10 @@
         override fun getShouldIconBeVisible(): Boolean {
             return shouldIconBeVisibleInternal
         }
+
+        override fun isCollecting(): Boolean {
+            return true
+        }
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 48b1732..481d453 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -38,6 +38,7 @@
 import com.android.internal.view.RotationPolicy;
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
@@ -55,10 +56,12 @@
 
     private static final String[] DEFAULT_SETTINGS = new String[]{"0:1", "2:0:1", "1:2"};
 
+    @Mock private DeviceStateManager mDeviceStateManager;
+    @Mock private DeviceStateRotationLockSettingControllerLogger mLogger;
+    @Mock private DumpManager mDumpManager;
+
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
-    @Mock
-    private DeviceStateManager mDeviceStateManager;
     private final RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
     private DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
     private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
@@ -78,7 +81,13 @@
         mSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(mContext);
         mDeviceStateRotationLockSettingController =
                 new DeviceStateRotationLockSettingController(
-                        mFakeRotationPolicy, mDeviceStateManager, mFakeExecutor, mSettingsManager);
+                        mFakeRotationPolicy,
+                        mDeviceStateManager,
+                        mFakeExecutor,
+                        mSettingsManager,
+                        mLogger,
+                        mDumpManager
+                );
 
         mDeviceStateRotationLockSettingController.setListening(true);
         verify(mDeviceStateManager)
@@ -173,15 +182,11 @@
     }
 
     @Test
-    public void whenDeviceStateSwitchedToIgnoredState_usePreviousSetting() {
-        initializeSettingsWith(
-                0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
-        mFakeRotationPolicy.setRotationLock(true);
-
-        mDeviceStateCallback.onStateChanged(1);
-        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
-
+    public void whenDeviceStateSwitchedToIgnoredState_useFallbackSetting() {
         mDeviceStateCallback.onStateChanged(0);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+
+        mDeviceStateCallback.onStateChanged(2);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
     }
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index a079668..c3a6cf0 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver
 import com.android.systemui.unfold.updates.FoldProvider
 import com.android.systemui.unfold.updates.RotationChangeProvider
+import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import com.android.systemui.unfold.util.CurrentActivityTypeProvider
 import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
@@ -67,6 +68,7 @@
     }
 
     val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
+    val hingeAngleProvider: HingeAngleProvider
     val rotationChangeProvider: RotationChangeProvider
 }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java b/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
index a94ab34..fa30a6f 100644
--- a/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
+++ b/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
@@ -255,7 +255,7 @@
         broadcastFilter.addAction(ACTION_FLASH_NOTIFICATION_STOP_PREVIEW);
         mFlashBroadcastReceiver = new FlashBroadcastReceiver();
         mContext.registerReceiver(
-                mFlashBroadcastReceiver, broadcastFilter, Context.RECEIVER_EXPORTED);
+                mFlashBroadcastReceiver, broadcastFilter, Context.RECEIVER_NOT_EXPORTED);
 
         final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index aaa376a..bffa3dd 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1672,57 +1672,62 @@
             return;
         }
 
-        synchronized (mRecords) {
-            String str = "notifyServiceStateForSubscriber: subId=" + subId + " phoneId=" + phoneId
-                    + " state=" + state;
-            if (VDBG) {
-                log(str);
-            }
-            mLocalLog.log(str);
-            // for service state updates, don't notify clients when subId is invalid. This prevents
-            // us from sending incorrect notifications like b/133140128
-            // In the future, we can remove this logic for every notification here and add a
-            // callback so listeners know when their PhoneStateListener's subId becomes invalid, but
-            // for now we use the simplest fix.
-            if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
-                mServiceState[phoneId] = state;
+        final long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mRecords) {
+                String str = "notifyServiceStateForSubscriber: subId=" + subId + " phoneId="
+                        + phoneId + " state=" + state;
+                if (VDBG) {
+                    log(str);
+                }
+                mLocalLog.log(str);
+                // for service state updates, don't notify clients when subId is invalid. This
+                // prevents us from sending incorrect notifications like b/133140128
+                // In the future, we can remove this logic for every notification here and add a
+                // callback so listeners know when their PhoneStateListener's subId becomes invalid,
+                // but for now we use the simplest fix.
+                if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
+                    mServiceState[phoneId] = state;
 
-                for (Record r : mRecords) {
-                    if (VDBG) {
-                        log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
-                                + " phoneId=" + phoneId + " state=" + state);
-                    }
-                    if (r.matchTelephonyCallbackEvent(
-                            TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
-                            && idMatch(r, subId, phoneId)) {
+                    for (Record r : mRecords) {
+                        if (VDBG) {
+                            log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
+                                    + " phoneId=" + phoneId + " state=" + state);
+                        }
+                        if (r.matchTelephonyCallbackEvent(
+                                TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
+                                && idMatch(r, subId, phoneId)) {
 
-                        try {
-                            ServiceState stateToSend;
-                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
-                                stateToSend = new ServiceState(state);
-                            } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
-                                stateToSend = state.createLocationInfoSanitizedCopy(false);
-                            } else {
-                                stateToSend = state.createLocationInfoSanitizedCopy(true);
+                            try {
+                                ServiceState stateToSend;
+                                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                    stateToSend = new ServiceState(state);
+                                } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                    stateToSend = state.createLocationInfoSanitizedCopy(false);
+                                } else {
+                                    stateToSend = state.createLocationInfoSanitizedCopy(true);
+                                }
+                                if (DBG) {
+                                    log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
+                                            + " subId=" + subId + " phoneId=" + phoneId
+                                            + " state=" + stateToSend);
+                                }
+                                r.callback.onServiceStateChanged(stateToSend);
+                            } catch (RemoteException ex) {
+                                mRemoveList.add(r.binder);
                             }
-                            if (DBG) {
-                                log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
-                                        + " subId=" + subId + " phoneId=" + phoneId
-                                        + " state=" + stateToSend);
-                            }
-                            r.callback.onServiceStateChanged(stateToSend);
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
                         }
                     }
+                } else {
+                    log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
+                            + " or subId=" + subId);
                 }
-            } else {
-                log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
-                        + " or subId=" + subId);
+                handleRemoveListLocked();
             }
-            handleRemoveListLocked();
+            broadcastServiceStateChanged(state, phoneId, subId);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
         }
-        broadcastServiceStateChanged(state, phoneId, subId);
     }
 
     public void notifySimActivationStateChangedForPhoneId(int phoneId, int subId,
@@ -3508,13 +3513,10 @@
     public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR";
 
     private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
-        final long ident = Binder.clearCallingIdentity();
         try {
             mBatteryStats.notePhoneState(state.getState());
         } catch (RemoteException re) {
             // Can't do much
-        } finally {
-            Binder.restoreCallingIdentity(ident);
         }
 
         // Send the broadcast exactly once to all possible disjoint sets of apps.
@@ -3531,8 +3533,7 @@
         // - Sanitized ServiceState sent to all other apps with READ_PHONE_STATE
         // - Sanitized ServiceState sent to all other apps with READ_PRIVILEGED_PHONE_STATE but not
         //   READ_PHONE_STATE
-        if (Binder.withCleanCallingIdentity(() ->
-                LocationAccessPolicy.isLocationModeEnabled(mContext, mContext.getUserId()))) {
+        if (LocationAccessPolicy.isLocationModeEnabled(mContext, mContext.getUserId())) {
             Intent fullIntent = createServiceStateIntent(state, subId, phoneId, false);
             mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
                     fullIntent,
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0ee883f..f236a96 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -402,6 +402,7 @@
 
     public void systemServicesReady() {
         mStats.systemServicesReady(mContext);
+        mCpuWakeupStats.systemServicesReady();
         mWorker.systemServicesReady();
         final INetworkManagementService nms = INetworkManagementService.Stub.asInterface(
                 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index bb8d3f4..23a384f 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -67,8 +67,8 @@
     private SparseIntArray mPendingUidStates = new SparseIntArray();
     private SparseIntArray mCapability = new SparseIntArray();
     private SparseIntArray mPendingCapability = new SparseIntArray();
-    private SparseBooleanArray mVisibleAppWidget = new SparseBooleanArray();
-    private SparseBooleanArray mPendingVisibleAppWidget = new SparseBooleanArray();
+    private SparseBooleanArray mAppWidgetVisible = new SparseBooleanArray();
+    private SparseBooleanArray mPendingAppWidgetVisible = new SparseBooleanArray();
     private SparseLongArray mPendingCommitTime = new SparseLongArray();
     private SparseBooleanArray mPendingGone = new SparseBooleanArray();
 
@@ -140,7 +140,7 @@
 
     private int evalModeInternal(int uid, int code, int uidState, int uidCapability) {
 
-        if (getUidVisibleAppWidget(uid) || mActivityManagerInternal.isPendingTopUid(uid)
+        if (getUidAppWidgetVisible(uid) || mActivityManagerInternal.isPendingTopUid(uid)
                 || mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid)) {
             return MODE_ALLOWED;
         }
@@ -205,7 +205,7 @@
         int numUids = uidPackageNames.size();
         for (int i = 0; i < numUids; i++) {
             int uid = uidPackageNames.keyAt(i);
-            mPendingVisibleAppWidget.put(uid, visible);
+            mPendingAppWidgetVisible.put(uid, visible);
 
             commitUidPendingState(uid);
         }
@@ -291,9 +291,9 @@
             ActivityManager.printCapabilitiesFull(pw, pendingCapability);
             pw.println();
         }
-        boolean appWidgetVisible = mVisibleAppWidget.get(uid, false);
+        boolean appWidgetVisible = mAppWidgetVisible.get(uid, false);
         // if no pendingAppWidgetVisible set to appWidgetVisible to suppress output
-        boolean pendingAppWidgetVisible = mPendingVisibleAppWidget.get(uid, appWidgetVisible);
+        boolean pendingAppWidgetVisible = mPendingAppWidgetVisible.get(uid, appWidgetVisible);
         pw.print("    appWidgetVisible=");
         pw.println(appWidgetVisible);
         if (appWidgetVisible != pendingAppWidgetVisible) {
@@ -333,25 +333,25 @@
                 mUidStates.get(uid, MIN_PRIORITY_UID_STATE));
         int pendingCapability = mPendingCapability.get(uid,
                 mCapability.get(uid, PROCESS_CAPABILITY_NONE));
-        boolean pendingVisibleAppWidget = mPendingVisibleAppWidget.get(uid,
-                mVisibleAppWidget.get(uid, false));
+        boolean pendingAppWidgetVisible = mPendingAppWidgetVisible.get(uid,
+                mAppWidgetVisible.get(uid, false));
 
         int uidState = mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
         int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
-        boolean visibleAppWidget = mVisibleAppWidget.get(uid, false);
+        boolean appWidgetVisible = mAppWidgetVisible.get(uid, false);
 
         if (uidState != pendingUidState
                 || capability != pendingCapability
-                || visibleAppWidget != pendingVisibleAppWidget) {
+                || appWidgetVisible != pendingAppWidgetVisible) {
             boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
                     != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
                     || capability != pendingCapability
-                    || visibleAppWidget != pendingVisibleAppWidget;
+                    || appWidgetVisible != pendingAppWidgetVisible;
 
             if (foregroundChange) {
                 // To save on memory usage, log only interesting changes.
                 mEventLog.logCommitUidState(uid, pendingUidState, pendingCapability,
-                        pendingVisibleAppWidget);
+                        pendingAppWidgetVisible, appWidgetVisible != pendingAppWidgetVisible);
             }
 
             for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
@@ -367,17 +367,17 @@
         if (mPendingGone.get(uid, false)) {
             mUidStates.delete(uid);
             mCapability.delete(uid);
-            mVisibleAppWidget.delete(uid);
+            mAppWidgetVisible.delete(uid);
             mPendingGone.delete(uid);
         } else {
             mUidStates.put(uid, pendingUidState);
             mCapability.put(uid, pendingCapability);
-            mVisibleAppWidget.put(uid, pendingVisibleAppWidget);
+            mAppWidgetVisible.put(uid, pendingAppWidgetVisible);
         }
 
         mPendingUidStates.delete(uid);
         mPendingCapability.delete(uid);
-        mPendingVisibleAppWidget.delete(uid);
+        mPendingAppWidgetVisible.delete(uid);
         mPendingCommitTime.delete(uid);
     }
 
@@ -385,8 +385,8 @@
         return mCapability.get(uid, ActivityManager.PROCESS_CAPABILITY_NONE);
     }
 
-    private boolean getUidVisibleAppWidget(int uid) {
-        return mVisibleAppWidget.get(uid, false);
+    private boolean getUidAppWidgetVisible(int uid) {
+        return mAppWidgetVisible.get(uid, false);
     }
 
     private static class EventLog {
@@ -398,6 +398,9 @@
         // Memory usage: 24 * size bytes
         private static final int EVAL_FOREGROUND_MODE_MAX_SIZE = 200;
 
+        private static final int APP_WIDGET_VISIBLE = 1 << 0;
+        private static final int APP_WIDGET_VISIBLE_CHANGED = 1 << 1;
+
         private final DelayableExecutor mExecutor;
         private final Thread mExecutorThread;
 
@@ -446,16 +449,18 @@
             mUpdateUidProcStateLogTimestamps[idx] = timestamp;
         }
 
-        void logCommitUidState(int uid, int uidState, int capability, boolean visible) {
+        void logCommitUidState(int uid, int uidState, int capability, boolean appWidgetVisible,
+                boolean appWidgetVisibleChanged) {
             if (COMMIT_UID_STATE_LOG_MAX_SIZE == 0) {
                 return;
             }
             mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logCommitUidStateAsync,
-                    this, System.currentTimeMillis(), uid, uidState, capability, visible));
+                    this, System.currentTimeMillis(), uid, uidState, capability, appWidgetVisible,
+                    appWidgetVisibleChanged));
         }
 
         void logCommitUidStateAsync(long timestamp, int uid, int uidState, int capability,
-                boolean visible) {
+                boolean appWidgetVisible, boolean appWidgetVisibleChanged) {
             int idx = (mCommitUidStateLogHead + mCommitUidStateLogSize)
                     % COMMIT_UID_STATE_LOG_MAX_SIZE;
             if (mCommitUidStateLogSize == COMMIT_UID_STATE_LOG_MAX_SIZE) {
@@ -468,7 +473,13 @@
             mCommitUidStateLog[idx][0] = uid;
             mCommitUidStateLog[idx][1] = uidState;
             mCommitUidStateLog[idx][2] = capability;
-            mCommitUidStateLog[idx][3] = visible ? 1 : 0;
+            mCommitUidStateLog[idx][3] = 0;
+            if (appWidgetVisible) {
+                mCommitUidStateLog[idx][3] += APP_WIDGET_VISIBLE;
+            }
+            if (appWidgetVisibleChanged) {
+                mCommitUidStateLog[idx][3] += APP_WIDGET_VISIBLE_CHANGED;
+            }
             mCommitUidStateLogTimestamps[idx] = timestamp;
         }
 
@@ -570,7 +581,9 @@
             int uid = mCommitUidStateLog[idx][0];
             int uidState = mCommitUidStateLog[idx][1];
             int capability = mCommitUidStateLog[idx][2];
-            boolean visibleAppWidget = mCommitUidStateLog[idx][3] != 0;
+            boolean appWidgetVisible = (mCommitUidStateLog[idx][3] & APP_WIDGET_VISIBLE) != 0;
+            boolean appWidgetVisibleChanged =
+                    (mCommitUidStateLog[idx][3] & APP_WIDGET_VISIBLE_CHANGED) != 0;
 
             TimeUtils.dumpTime(pw, timestamp);
 
@@ -585,8 +598,12 @@
             pw.print(" capability=");
             pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
 
-            pw.print(" visibleAppWidget=");
-            pw.print(visibleAppWidget);
+            pw.print(" appWidgetVisible=");
+            pw.print(appWidgetVisible);
+
+            if (appWidgetVisibleChanged) {
+                pw.print(" (changed)");
+            }
 
             pw.println();
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0d0e576..c50e275 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8588,7 +8588,9 @@
                     if (isMutable()) {
                         // For call stream, align mute only when muted, not when index is set to 0
                         mVolumeGroupState.mute(
-                                forceMuteState ? mIsMuted : groupIndex == 0 || mIsMuted);
+                                forceMuteState ? mIsMuted :
+                                        (groupIndex == 0 && !isCallStream(mStreamType))
+                                                || mIsMuted);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 55d2921..ea157c8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -128,6 +128,7 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.Spline;
+import android.view.ContentRecordingSession;
 import android.view.Display;
 import android.view.DisplayEventReceiver;
 import android.view.DisplayInfo;
@@ -250,6 +251,7 @@
     private ActivityManagerInternal mActivityManagerInternal;
     private ActivityManager mActivityManager;
     private UidImportanceListener mUidImportanceListener = new UidImportanceListener();
+    @Nullable
     private IMediaProjectionManager mProjectionService;
     private DeviceStateManagerInternal mDeviceStateManager;
     @GuardedBy("mSyncRoot")
@@ -1494,8 +1496,9 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
+            final int displayId;
             synchronized (mSyncRoot) {
-                final int displayId =
+                displayId =
                         createVirtualDisplayLocked(
                                 callback,
                                 projection,
@@ -1509,8 +1512,39 @@
                     mDisplayWindowPolicyControllers.put(
                             displayId, Pair.create(virtualDevice, dwpc));
                 }
-                return displayId;
             }
+
+            // When calling setContentRecordingSession into the WindowManagerService, the WMS
+            // attempts to acquire a lock before executing its main body. Due to this, we need
+            // to be sure that it isn't called while the DisplayManagerService is also holding
+            // a lock, to avoid a deadlock scenario.
+            final ContentRecordingSession session =
+                    virtualDisplayConfig.getContentRecordingSession();
+
+            if (displayId != Display.INVALID_DISPLAY && session != null) {
+                // Only attempt to set content recording session if there are details to set and a
+                // VirtualDisplay has been successfully constructed.
+                session.setDisplayId(displayId);
+
+                // We set the content recording session here on the server side instead of using
+                // a second AIDL call in MediaProjection. By ensuring that a virtual display has
+                // been constructed before calling setContentRecordingSession, we avoid a race
+                // condition between the DMS & WMS which could lead to the MediaProjection
+                // being pre-emptively torn down.
+                if (!mWindowManagerInternal.setContentRecordingSession(session)) {
+                    // Unable to start mirroring, so tear down projection & release VirtualDisplay.
+                    try {
+                        getProjectionService().stopActiveProjection();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to tell MediaProjectionManagerService to stop the "
+                                + "active projection", e);
+                    }
+                    releaseVirtualDisplayInternal(callback.asBinder());
+                    return Display.INVALID_DISPLAY;
+                }
+            }
+
+            return displayId;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -2804,8 +2838,7 @@
 
     private IMediaProjectionManager getProjectionService() {
         if (mProjectionService == null) {
-            IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
-            mProjectionService = IMediaProjectionManager.Stub.asInterface(b);
+            mProjectionService = mInjector.getProjectionService();
         }
         return mProjectionService;
     }
@@ -2964,6 +2997,11 @@
         boolean getHdrOutputConversionSupport() {
             return DisplayControl.getHdrOutputConversionSupport();
         }
+
+        IMediaProjectionManager getProjectionService() {
+            IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
+            return  IMediaProjectionManager.Stub.asInterface(b);
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index eda15ae..4f7a2ba 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -88,7 +88,17 @@
     // Called with SyncRoot lock held.
     public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener) {
-        this(syncRoot, context, handler, listener, DisplayControl::createDisplay);
+        this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() {
+            @Override
+            public IBinder createDisplay(String name, boolean secure, float requestedRefreshRate) {
+                return DisplayControl.createDisplay(name, secure, requestedRefreshRate);
+            }
+
+            @Override
+            public void destroyDisplay(IBinder displayToken) {
+                DisplayControl.destroyDisplay(displayToken);
+            }
+        });
     }
 
     @VisibleForTesting
@@ -311,7 +321,7 @@
                 mSurface.release();
                 mSurface = null;
             }
-            DisplayControl.destroyDisplay(getDisplayTokenLocked());
+            mSurfaceControlDisplayFactory.destroyDisplay(getDisplayTokenLocked());
             if (mProjection != null && mMediaProjectionCallback != null) {
                 try {
                     mProjection.unregisterCallback(mMediaProjectionCallback);
@@ -653,5 +663,12 @@
          * @return The token reference for the display in SurfaceFlinger.
          */
         IBinder createDisplay(String name, boolean secure, float requestedRefreshRate);
+        
+        /**
+         * Destroy a display in SurfaceFlinger.
+         *
+         * @param displayToken The display token for the display to be destroyed.
+         */
+        void destroyDisplay(IBinder displayToken);
     }
 }
diff --git a/services/core/java/com/android/server/dreams/DreamShellCommand.java b/services/core/java/com/android/server/dreams/DreamShellCommand.java
index ab84ae4..df70a32 100644
--- a/services/core/java/com/android/server/dreams/DreamShellCommand.java
+++ b/services/core/java/com/android/server/dreams/DreamShellCommand.java
@@ -39,26 +39,24 @@
 
     @Override
     public int onCommand(String cmd) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != Process.ROOT_UID) {
-            Slog.e(TAG, "Must be root before calling Dream shell commands");
-            return -1;
-        }
-
-        if (TextUtils.isEmpty(cmd)) {
-            return super.handleDefaultCommands(cmd);
-        }
         if (DEBUG) {
             Slog.d(TAG, "onCommand:" + cmd);
         }
 
-        switch (cmd) {
-            case "start-dreaming":
-                return startDreaming();
-            case "stop-dreaming":
-                return stopDreaming();
-            default:
-                return super.handleDefaultCommands(cmd);
+        try {
+            switch (cmd) {
+                case "start-dreaming":
+                    enforceCallerIsRoot();
+                    return startDreaming();
+                case "stop-dreaming":
+                    enforceCallerIsRoot();
+                    return stopDreaming();
+                default:
+                    return super.handleDefaultCommands(cmd);
+            }
+        } catch (SecurityException e) {
+            getOutPrintWriter().println(e);
+            return -1;
         }
     }
 
@@ -72,6 +70,12 @@
         return 0;
     }
 
+    private void enforceCallerIsRoot() {
+        if (Binder.getCallingUid() != Process.ROOT_UID) {
+            throw new SecurityException("Must be root to call Dream shell commands");
+        }
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 2174f40..c962bc4 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -295,6 +295,7 @@
         }
 
         ipw.println("Capabilities: " + mGnssNative.getCapabilities());
+        ipw.println("GNSS Hardware Model Name: " + getGnssHardwareModelName());
 
         if (mGnssStatusProvider.isSupported()) {
             ipw.println("Status Provider:");
diff --git a/services/core/java/com/android/server/media/AudioAttributesUtils.java b/services/core/java/com/android/server/media/AudioAttributesUtils.java
new file mode 100644
index 0000000..b9c9bae
--- /dev/null
+++ b/services/core/java/com/android/server/media/AudioAttributesUtils.java
@@ -0,0 +1,112 @@
+/*
+ * 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.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioAttributes;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.MediaRoute2Info;
+
+/* package */ final class AudioAttributesUtils {
+
+    /* package */ static final AudioAttributes ATTRIBUTES_MEDIA = new AudioAttributes.Builder()
+            .setUsage(AudioAttributes.USAGE_MEDIA)
+            .build();
+
+    private AudioAttributesUtils() {
+        // no-op to prevent instantiation.
+    }
+
+    @MediaRoute2Info.Type
+    /* package */ static int mapToMediaRouteType(
+            @NonNull AudioDeviceAttributes audioDeviceAttributes) {
+        switch (audioDeviceAttributes.getType()) {
+            case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
+            case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
+                return MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+            case AudioDeviceInfo.TYPE_WIRED_HEADSET:
+                return MediaRoute2Info.TYPE_WIRED_HEADSET;
+            case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
+                return MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+            case AudioDeviceInfo.TYPE_DOCK:
+            case AudioDeviceInfo.TYPE_DOCK_ANALOG:
+                return MediaRoute2Info.TYPE_DOCK;
+            case AudioDeviceInfo.TYPE_HDMI:
+                return MediaRoute2Info.TYPE_HDMI;
+            case AudioDeviceInfo.TYPE_USB_DEVICE:
+                return MediaRoute2Info.TYPE_USB_DEVICE;
+            case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
+                return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+            case AudioDeviceInfo.TYPE_BLE_HEADSET:
+                return MediaRoute2Info.TYPE_BLE_HEADSET;
+            case AudioDeviceInfo.TYPE_HEARING_AID:
+                return MediaRoute2Info.TYPE_HEARING_AID;
+            default:
+                return MediaRoute2Info.TYPE_UNKNOWN;
+        }
+    }
+
+
+    /* package */ static boolean isDeviceOutputAttributes(
+            @Nullable AudioDeviceAttributes audioDeviceAttributes) {
+        if (audioDeviceAttributes == null) {
+            return false;
+        }
+
+        if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
+            return false;
+        }
+
+        switch (audioDeviceAttributes.getType()) {
+            case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
+            case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
+            case AudioDeviceInfo.TYPE_WIRED_HEADSET:
+            case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
+            case AudioDeviceInfo.TYPE_DOCK:
+            case AudioDeviceInfo.TYPE_DOCK_ANALOG:
+            case AudioDeviceInfo.TYPE_HDMI:
+            case AudioDeviceInfo.TYPE_USB_DEVICE:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /* package */ static boolean isBluetoothOutputAttributes(
+            @Nullable AudioDeviceAttributes audioDeviceAttributes) {
+        if (audioDeviceAttributes == null) {
+            return false;
+        }
+
+        if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
+            return false;
+        }
+
+        switch (audioDeviceAttributes.getType()) {
+            case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
+            case AudioDeviceInfo.TYPE_BLE_HEADSET:
+            case AudioDeviceInfo.TYPE_BLE_SPEAKER:
+            case AudioDeviceInfo.TYPE_HEARING_AID:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothRouteController.java
index d4a1184..66985e0 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteController.java
@@ -53,7 +53,16 @@
             return new NoOpBluetoothRouteController();
         }
 
-        return new LegacyBluetoothRouteController(context, btAdapter, listener);
+        MediaFeatureFlagManager flagManager = MediaFeatureFlagManager.getInstance();
+        boolean isUsingLegacyController = flagManager.getBoolean(
+                MediaFeatureFlagManager.FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER,
+                true);
+
+        if (isUsingLegacyController) {
+            return new LegacyBluetoothRouteController(context, btAdapter, listener);
+        } else {
+            return new AudioPoliciesBluetoothRouteController(context, btAdapter, listener);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index d7893ee..3875c84 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -44,10 +44,22 @@
         IAudioService audioService = IAudioService.Stub.asInterface(
                 ServiceManager.getService(Context.AUDIO_SERVICE));
 
-        return new LegacyDeviceRouteController(context,
-                audioManager,
-                audioService,
-                onDeviceRouteChangedListener);
+        MediaFeatureFlagManager flagManager = MediaFeatureFlagManager.getInstance();
+        boolean isUsingLegacyController = flagManager.getBoolean(
+                MediaFeatureFlagManager.FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER,
+                true);
+
+        if (isUsingLegacyController) {
+            return new LegacyDeviceRouteController(context,
+                    audioManager,
+                    audioService,
+                    onDeviceRouteChangedListener);
+        } else {
+            return new AudioPoliciesDeviceRouteController(context,
+                    audioManager,
+                    audioService,
+                    onDeviceRouteChangedListener);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/media/MediaFeatureFlagManager.java b/services/core/java/com/android/server/media/MediaFeatureFlagManager.java
index 723cda0..70ee38f 100644
--- a/services/core/java/com/android/server/media/MediaFeatureFlagManager.java
+++ b/services/core/java/com/android/server/media/MediaFeatureFlagManager.java
@@ -32,7 +32,7 @@
     private static final String NAMESPACE_MEDIA_BETTER_TOGETHER = "media_better_together";
 
     @StringDef(prefix = "FEATURE_", value = {
-        FEATURE_IS_USING_LEGACY_BLUETOOTH_CONTROLLER
+            FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER
     })
     @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
     @Retention(RetentionPolicy.SOURCE)
@@ -43,7 +43,7 @@
      * 'Audio Strategies'-aware controller.
      */
     /* package */ static final @MediaFeatureFlag String
-            FEATURE_IS_USING_LEGACY_BLUETOOTH_CONTROLLER =
+            FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER =
             "BluetoothRouteController__enable_legacy_bluetooth_routes_controller";
 
     private static final MediaFeatureFlagManager sInstance = new MediaFeatureFlagManager();
@@ -52,7 +52,7 @@
         // Empty to prevent instantiation.
     }
 
-    /* package */ MediaFeatureFlagManager getInstance() {
+    /* package */ static MediaFeatureFlagManager getInstance() {
         return sInstance;
     }
 
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6619e6c..5d5c621 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -16,11 +16,14 @@
 
 package com.android.server.media;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.media.AudioAttributes;
+import android.media.AudioDeviceAttributes;
 import android.media.AudioManager;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
@@ -37,6 +40,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -71,6 +75,26 @@
     private final AudioManagerBroadcastReceiver mAudioReceiver =
             new AudioManagerBroadcastReceiver();
 
+    private final AudioManager.OnDevicesForAttributesChangedListener
+            mOnDevicesForAttributesChangedListener =
+            new AudioManager.OnDevicesForAttributesChangedListener() {
+                @Override
+                public void onDevicesForAttributesChanged(@NonNull AudioAttributes attributes,
+                        @NonNull List<AudioDeviceAttributes> devices) {
+                    if (attributes.getUsage() != AudioAttributes.USAGE_MEDIA) {
+                        return;
+                    }
+
+                    mHandler.post(() -> {
+                        updateSelectedAudioDevice(devices);
+                        notifyProviderState();
+                        if (updateSessionInfosIfNeeded()) {
+                            notifySessionInfoUpdated();
+                        }
+                    });
+                }
+            };
+
     private final Object mRequestLock = new Object();
     @GuardedBy("mRequestLock")
     private volatile SessionCreationRequest mPendingSessionCreationRequest;
@@ -100,8 +124,15 @@
             });
         });
 
+        mAudioManager.addOnDevicesForAttributesChangedListener(
+                AudioAttributesUtils.ATTRIBUTES_MEDIA, mContext.getMainExecutor(),
+                mOnDevicesForAttributesChangedListener);
+
         // These methods below should be called after all fields are initialized, as they
         // access the fields inside.
+        List<AudioDeviceAttributes> devices =
+                mAudioManager.getDevicesForAttributes(AudioAttributesUtils.ATTRIBUTES_MEDIA);
+        updateSelectedAudioDevice(devices);
         updateProviderState();
         updateSessionInfosIfNeeded();
     }
@@ -239,6 +270,26 @@
         }
     }
 
+    private void updateSelectedAudioDevice(@NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            Slog.w(TAG, "The list of preferred devices was empty.");
+            return;
+        }
+
+        AudioDeviceAttributes audioDeviceAttributes = devices.get(0);
+
+        if (AudioAttributesUtils.isDeviceOutputAttributes(audioDeviceAttributes)) {
+            mDeviceRouteController.selectRoute(
+                    AudioAttributesUtils.mapToMediaRouteType(audioDeviceAttributes));
+            mBluetoothRouteController.selectRoute(null);
+        } else if (AudioAttributesUtils.isBluetoothOutputAttributes(audioDeviceAttributes)) {
+            mDeviceRouteController.selectRoute(null);
+            mBluetoothRouteController.selectRoute(audioDeviceAttributes.getAddress());
+        } else {
+            Slog.w(TAG, "Unknown audio attributes: " + audioDeviceAttributes);
+        }
+    }
+
     private void updateProviderState() {
         MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
 
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9e01c7a..84bee50 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -39,6 +39,7 @@
 import android.app.AppGlobals;
 import android.app.IApplicationThread;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyCache;
 import android.app.admin.DevicePolicyManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ActivityNotFoundException;
@@ -85,6 +86,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
@@ -107,6 +109,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 
@@ -623,7 +626,7 @@
                     // package does not exist; should not happen
                     return null;
                 }
-                return new LauncherActivityInfoInternal(activityInfo, incrementalStatesInfo);
+                return new LauncherActivityInfoInternal(activityInfo, incrementalStatesInfo, user);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -676,7 +679,7 @@
                     continue;
                 }
                 results.add(new LauncherActivityInfoInternal(ri.activityInfo,
-                        incrementalStatesInfo));
+                        incrementalStatesInfo, user));
             }
             return results;
         }
@@ -1078,6 +1081,55 @@
         }
 
         @Override
+        @NonNull
+        public Map<String, LauncherActivityInfoInternal> getActivityOverrides(String callingPackage,
+                int userId) {
+            ensureShortcutPermission(callingPackage);
+            int callingUid = Binder.getCallingUid();
+            final long callerIdentity = Binder.clearCallingIdentity();
+            try {
+                Map<String, LauncherActivityInfoInternal> shortcutOverridesInfo = new ArrayMap<>();
+                UserHandle managedUserHandle = getManagedProfile(userId);
+                if (managedUserHandle == null) {
+                    return shortcutOverridesInfo;
+                }
+
+                List<String> packagesToOverride =
+                        DevicePolicyCache.getInstance().getLauncherShortcutOverrides();
+                for (String packageName : packagesToOverride) {
+                    Intent intent = new Intent(Intent.ACTION_MAIN)
+                            .addCategory(Intent.CATEGORY_LAUNCHER)
+                            .setPackage(packageName);
+
+                    List<LauncherActivityInfoInternal> possibleShortcutOverrides =
+                            queryIntentLauncherActivities(
+                                    intent,
+                                    callingUid,
+                                    managedUserHandle
+                            );
+
+                    if (!possibleShortcutOverrides.isEmpty()) {
+                        shortcutOverridesInfo.put(packageName, possibleShortcutOverrides.get(0));
+                    }
+                }
+                return shortcutOverridesInfo;
+            } finally {
+                Binder.restoreCallingIdentity(callerIdentity);
+            }
+        }
+
+
+        @Nullable
+        private UserHandle getManagedProfile(int userId) {
+            for (UserInfo profile : mUm.getProfiles(userId)) {
+                if (profile.isManagedProfile()) {
+                    return profile.getUserHandle();
+                }
+            }
+            return null;
+        }
+
+        @Override
         public boolean startShortcut(String callingPackage, String packageName, String featureId,
                 String shortcutId, Rect sourceBounds, Bundle startActivityOptions,
                 int targetUserId) {
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
index 706aedc..b05b662 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -22,7 +22,9 @@
 
 import android.content.Context;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.LongSparseArray;
@@ -40,6 +42,8 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -52,13 +56,13 @@
     private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
     private static final String SUBSYSTEM_ALARM_WIFI = "Wifi";
     @VisibleForTesting
-    static final long WAKEUP_RETENTION_MS = 3 * 24 * 60 * 60_000; // 3 days.
-    @VisibleForTesting
     static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
-    private static final long WAKEUP_WRITE_DELAY_MS = 2 * 60 * 1000;  // 2 minutes.
+    private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.MINUTES.toMillis(2);
 
     private final Handler mHandler;
     private final IrqDeviceMap mIrqDeviceMap;
+    @VisibleForTesting
+    final Config mConfig = new Config();
     private final WakingActivityHistory mRecentWakingActivity = new WakingActivityHistory();
 
     @VisibleForTesting
@@ -72,6 +76,14 @@
         mHandler = handler;
     }
 
+    /**
+     * Called on the boot phase SYSTEM_SERVICES_READY.
+     * This ensures that DeviceConfig is ready for calls to read properties.
+     */
+    public synchronized void systemServicesReady() {
+        mConfig.register(new HandlerExecutor(mHandler));
+    }
+
     private static int subsystemToStatsReason(int subsystem) {
         switch (subsystem) {
             case CPU_WAKEUP_SUBSYSTEM_ALARM:
@@ -136,14 +148,15 @@
         // we can delete all history that will not be useful in attributing future wakeups.
         mRecentWakingActivity.clearAllBefore(elapsedRealtime - WAKEUP_REASON_HALF_WINDOW_MS);
 
-        // Limit history of wakeups and their attribution to the last WAKEUP_RETENTION_MS. Note that
+        // Limit history of wakeups and their attribution to the last retentionDuration. Note that
         // the last wakeup and its attribution (if computed) is always stored, even if that wakeup
-        // had occurred before WAKEUP_RETENTION_MS.
-        int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+        // had occurred before retentionDuration.
+        final long retentionDuration = mConfig.WAKEUP_STATS_RETENTION_MS;
+        int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - retentionDuration);
         for (int i = lastIdx; i >= 0; i--) {
             mWakeupEvents.removeAt(i);
         }
-        lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+        lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - retentionDuration);
         for (int i = lastIdx; i >= 0; i--) {
             mWakeupAttribution.removeAt(i);
         }
@@ -226,6 +239,9 @@
         pw.println("CPU wakeup stats:");
         pw.increaseIndent();
 
+        mConfig.dump(pw);
+        pw.println();
+
         mIrqDeviceMap.dump(pw);
         pw.println();
 
@@ -296,7 +312,8 @@
     }
 
     private static final class WakingActivityHistory {
-        private static final long WAKING_ACTIVITY_RETENTION_MS = 3 * 60 * 60_000; // 3 hours.
+        private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10);
+
         private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity =
                 new SparseArray<>();
 
@@ -521,4 +538,52 @@
             }
         }
     }
+
+    static final class Config implements DeviceConfig.OnPropertiesChangedListener {
+        static final String KEY_WAKEUP_STATS_RETENTION_MS = "wakeup_stats_retention_ms";
+
+        private static final String[] PROPERTY_NAMES = {
+                KEY_WAKEUP_STATS_RETENTION_MS,
+        };
+
+        static final long DEFAULT_WAKEUP_STATS_RETENTION_MS = TimeUnit.DAYS.toMillis(3);
+
+        /**
+         * Wakeup stats are retained only for this duration.
+         */
+        public volatile long WAKEUP_STATS_RETENTION_MS = DEFAULT_WAKEUP_STATS_RETENTION_MS;
+
+        void register(Executor executor) {
+            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_STATS,
+                    executor, this);
+            onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BATTERY_STATS,
+                    PROPERTY_NAMES));
+        }
+
+        @Override
+        public void onPropertiesChanged(DeviceConfig.Properties properties) {
+            for (String name : properties.getKeyset()) {
+                if (name == null) {
+                    continue;
+                }
+                switch (name) {
+                    case KEY_WAKEUP_STATS_RETENTION_MS:
+                        WAKEUP_STATS_RETENTION_MS = properties.getLong(
+                                KEY_WAKEUP_STATS_RETENTION_MS, DEFAULT_WAKEUP_STATS_RETENTION_MS);
+                        break;
+                }
+            }
+        }
+
+        void dump(IndentingPrintWriter pw) {
+            pw.println("Config:");
+
+            pw.increaseIndent();
+
+            pw.print(KEY_WAKEUP_STATS_RETENTION_MS, WAKEUP_STATS_RETENTION_MS);
+            pw.println();
+
+            pw.decreaseIndent();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 5bace0e..88d64df 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -151,6 +151,13 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
     static final long REQUEST_LISTENING_MUST_MATCH_PACKAGE = 172251878L;
 
+    /**
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    static final long REQUEST_LISTENING_OTHER_USER_NOOP = 242194868L;
+
     private final Context mContext;
 
     private final Handler mHandler = new Handler();
@@ -1888,7 +1895,12 @@
 
             // Check current user
             if (userId != currentUser) {
-                throw new IllegalArgumentException("User " + userId + " is not the current user.");
+                if (CompatChanges.isChangeEnabled(REQUEST_LISTENING_OTHER_USER_NOOP, callingUid)) {
+                    return;
+                } else {
+                    throw new IllegalArgumentException(
+                            "User " + userId + " is not the current user.");
+                }
             }
         }
         if (mBar != null) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 404ca01..c6684df 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2125,6 +2125,26 @@
         }
 
         @Override
+        public void notifyTvMessage(IBinder sessionToken, String type, Bundle data, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "timeShiftEnablePositionTracking");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .notifyTvMessage(type, data);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in timeShiftEnablePositionTracking", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void startRecording(IBinder sessionToken, @Nullable Uri programUri,
                 @Nullable Bundle params, int userId) {
             final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 37e4890..41fee1c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8145,7 +8145,13 @@
         }
 
         // Clear config override in #updateCompatDisplayInsets().
-        onRequestedOverrideConfigurationChanged(EMPTY);
+        final int activityType = getActivityType();
+        final Configuration overrideConfig = getRequestedOverrideConfiguration();
+        overrideConfig.unset();
+        // Keep the activity type which was set when attaching to a task to prevent leaving it
+        // undefined.
+        overrideConfig.windowConfiguration.setActivityType(activityType);
+        onRequestedOverrideConfigurationChanged(overrideConfig);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index df471c5..710c4af 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1729,14 +1729,11 @@
         }
 
         if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
-            Toast toast = Toast.makeText(mService.mContext,
+            UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
                     (ActivitySecurityModelFeatureFlags.DOC_LINK
-                            + (restrictActivitySwitch
-                            ? "returned home due to "
-                            : "would return home due to ")
-                            + callingLabel),
-                    Toast.LENGTH_LONG);
-            UiThread.getHandler().post(toast::show);
+                            + (restrictActivitySwitch ? " returned home due to "
+                                    : " would return home due to ")
+                            + callingLabel), Toast.LENGTH_LONG).show());
         }
 
         // If the activity switch should be restricted, return home rather than the
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 09f7fb6..cf0fc09 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2018,16 +2018,19 @@
 
     /**
      * Like isOnScreen(), but we don't return true if the window is part
-     * of a transition that has not yet been started.
+     * of a transition but has not yet started animating.
      */
     boolean isReadyForDisplay() {
-        if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) {
+        if (!mHasSurface || mDestroying || !isVisibleByPolicy()) {
+            return false;
+        }
+        if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()
+                && !isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)) {
             return false;
         }
         final boolean parentAndClientVisible = !isParentWindowHidden()
                 && mViewVisibility == View.VISIBLE && mToken.isVisible();
-        return mHasSurface && isVisibleByPolicy() && !mDestroying
-                && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
+        return parentAndClientVisible || isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_ALL);
     }
 
     boolean isFullyTransparent() {
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index c8ec7c2..e84f0cc 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -20,17 +20,18 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.credentials.ClearCredentialStateRequest;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.IClearCredentialStateCallback;
 import android.credentials.ui.ProviderData;
 import android.credentials.ui.RequestInfo;
 import android.os.CancellationSignal;
 import android.os.RemoteException;
 import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
 import android.util.Log;
 
 import com.android.server.credentials.metrics.ApiName;
 import com.android.server.credentials.metrics.ApiStatus;
+import com.android.server.credentials.metrics.ProviderStatusForMetrics;
 
 import java.util.ArrayList;
 
@@ -120,22 +121,22 @@
         Log.i(TAG, "respondToClientWithResponseAndFinish");
         if (isSessionCancelled()) {
             mChosenProviderMetric.setChosenProviderStatus(
-                    MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
+                    ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode());
             logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+                    ApiStatus.CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
             return;
         }
         try {
             mClientCallback.onSuccess();
             logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_SUCCESS);
+                    ApiStatus.SUCCESS);
         } catch (RemoteException e) {
             mChosenProviderMetric.setChosenProviderStatus(
-                    MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE);
+                    ProviderStatusForMetrics.FINAL_FAILURE.getMetricCode());
             Log.i(TAG, "Issue while propagating the response to the client");
             logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_FAILURE);
+                    ApiStatus.FAILURE);
         }
         finishSession(/*propagateCancellation=*/false);
     }
@@ -144,7 +145,7 @@
         Log.i(TAG, "respondToClientWithErrorAndFinish");
         if (isSessionCancelled()) {
             logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+                    ApiStatus.CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
             return;
         }
@@ -154,7 +155,7 @@
             e.printStackTrace();
         }
         logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
-                ApiStatus.METRICS_API_STATUS_FAILURE);
+                ApiStatus.FAILURE);
         finishSession(/*propagateCancellation=*/false);
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 0c1133c..7e1780d 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -16,9 +16,6 @@
 
 package com.android.server.credentials;
 
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
@@ -27,17 +24,18 @@
 import android.credentials.CreateCredentialRequest;
 import android.credentials.CreateCredentialResponse;
 import android.credentials.CredentialManager;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.ICreateCredentialCallback;
 import android.credentials.ui.ProviderData;
 import android.credentials.ui.RequestInfo;
 import android.os.CancellationSignal;
 import android.os.RemoteException;
 import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
 import android.util.Log;
 
 import com.android.server.credentials.metrics.ApiName;
 import com.android.server.credentials.metrics.ApiStatus;
+import com.android.server.credentials.metrics.ProviderStatusForMetrics;
 
 import java.util.ArrayList;
 
@@ -103,11 +101,11 @@
         setChosenMetric(componentName);
         if (response != null) {
             mChosenProviderMetric.setChosenProviderStatus(
-                    METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
+                    ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode());
             respondToClientWithResponseAndFinish(response);
         } else {
             mChosenProviderMetric.setChosenProviderStatus(
-                    METRICS_PROVIDER_STATUS_FINAL_FAILURE);
+                    ProviderStatusForMetrics.FINAL_FAILURE.getMetricCode());
             respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS,
                     "Invalid response");
         }
@@ -144,18 +142,18 @@
         }
         if (isSessionCancelled()) {
             logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+                    ApiStatus.CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
             return;
         }
         try {
             mClientCallback.onResponse(response);
             logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_SUCCESS);
+                    ApiStatus.SUCCESS);
         } catch (RemoteException e) {
             Log.i(TAG, "Issue while responding to client: " + e.getMessage());
             logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_FAILURE);
+                    ApiStatus.FAILURE);
         }
         finishSession(/*propagateCancellation=*/false);
     }
@@ -168,7 +166,7 @@
         }
         if (isSessionCancelled()) {
             logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+                    ApiStatus.CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
             return;
         }
@@ -184,10 +182,10 @@
     private void logFailureOrUserCancel(String errorType) {
         if (CreateCredentialException.TYPE_USER_CANCELED.equals(errorType)) {
             logApiCall(ApiName.CREATE_CREDENTIAL,
-                    /* apiStatus */ ApiStatus.METRICS_API_STATUS_USER_CANCELED);
+                    /* apiStatus */ ApiStatus.USER_CANCELED);
         } else {
             logApiCall(ApiName.CREATE_CREDENTIAL,
-                    /* apiStatus */ ApiStatus.METRICS_API_STATUS_FAILURE);
+                    /* apiStatus */ ApiStatus.FAILURE);
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 41ae911..9c87005 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -36,15 +36,14 @@
 import android.credentials.CredentialDescription;
 import android.credentials.CredentialManager;
 import android.credentials.CredentialOption;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialRequest;
 import android.credentials.IClearCredentialStateCallback;
 import android.credentials.ICreateCredentialCallback;
 import android.credentials.ICredentialManager;
 import android.credentials.IGetCredentialCallback;
-import android.credentials.IListEnabledProvidersCallback;
 import android.credentials.ISetEnabledProvidersCallback;
-import android.credentials.ListEnabledProvidersResponse;
 import android.credentials.RegisterCredentialDescriptionRequest;
 import android.credentials.UnregisterCredentialDescriptionRequest;
 import android.credentials.ui.IntentFactory;
@@ -56,7 +55,7 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
+import android.service.credentials.CredentialProviderInfoFactory;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -118,10 +117,11 @@
             int resolvedUserId) {
         List<CredentialManagerServiceImpl> services = new ArrayList<>();
         List<CredentialProviderInfo> serviceInfos =
-                CredentialProviderInfo.getAvailableSystemServices(
+                CredentialProviderInfoFactory.getAvailableSystemServices(
                         mContext,
                         resolvedUserId,
-                        /* disableSystemAppVerificationForTests= */ false);
+                        /* disableSystemAppVerificationForTests= */ false,
+                        new HashSet<>());
         serviceInfos.forEach(
                 info -> {
                     services.add(
@@ -222,10 +222,16 @@
         return hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
     }
 
-    private void verifyPermission(String permission) throws SecurityException {
-        if (!hasPermission(permission)) {
-            throw new SecurityException("Caller is missing permission: " + permission);
+    private void verifyGetProvidersPermission() throws SecurityException {
+        if (hasPermission(android.Manifest.permission.QUERY_ALL_PACKAGES)) {
+            return;
         }
+
+        if (hasPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)) {
+            return;
+        }
+        
+        throw new SecurityException("Caller is missing permission: QUERY_ALL_PACKAGES or LIST_ENABLED_CREDENTIAL_PROVIDERS");
     }
 
     private boolean hasPermission(String permission) {
@@ -550,40 +556,6 @@
             providerSessions.forEach(ProviderSession::invokeSession);
         }
 
-        @SuppressWarnings("GuardedBy") // ErrorProne requires listEnabledProviders
-        // to be guarded by 'service.mLock', which is the same as mLock.
-        @Override
-        public ICancellationSignal listEnabledProviders(IListEnabledProvidersCallback callback) {
-            Log.i(TAG, "listEnabledProviders");
-            ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-
-            if (!hasWriteSecureSettingsPermission()) {
-                try {
-                    callback.onError(
-                            PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Issue with invoking response: " + e.getMessage());
-                }
-                return cancelTransport;
-            }
-
-            List<String> enabledProviders = new ArrayList<>();
-            runForUser(
-                    (service) -> {
-                        enabledProviders.add(service.getComponentName().flattenToString());
-                    });
-
-            // Call the callback.
-            try {
-                callback.onResponse(ListEnabledProvidersResponse.create(enabledProviders));
-            } catch (RemoteException e) {
-                Log.i(TAG, "Issue with invoking response: " + e.getMessage());
-                // TODO: Propagate failure
-            }
-
-            return cancelTransport;
-        }
-
         @Override
         public void setEnabledProviders(
                 List<String> providers, int userId, ISetEnabledProvidersCallback callback) {
@@ -659,7 +631,7 @@
                             // The component name and the package name do not match.
                             MetricUtilities.logApiCalled(
                                     ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
-                                    ApiStatus.METRICS_API_STATUS_FAILURE, callingUid);
+                                    ApiStatus.FAILURE, callingUid);
                             Log.w(
                                     TAG,
                                     "isEnabledCredentialProviderService: Component name does not"
@@ -667,7 +639,7 @@
                             return false;
                         }
                         MetricUtilities.logApiCalled(ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
-                                ApiStatus.METRICS_API_STATUS_SUCCESS, callingUid);
+                                ApiStatus.SUCCESS, callingUid);
                         return true;
                     }
                 }
@@ -677,20 +649,35 @@
         }
 
         @Override
-        public List<ServiceInfo> getCredentialProviderServices(
-                int userId, boolean disableSystemAppVerificationForTests, int providerFilter) {
+        public List<CredentialProviderInfo> getCredentialProviderServices(
+                int userId, int providerFilter) {
             Log.i(TAG, "getCredentialProviderServices");
-            verifyPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS);
+            verifyGetProvidersPermission();
 
-            List<ServiceInfo> services = new ArrayList<>();
-            List<CredentialProviderInfo> providers =
-                    CredentialProviderInfo.getCredentialProviderServices(
-                            mContext, userId, disableSystemAppVerificationForTests, providerFilter);
-            for (CredentialProviderInfo p : providers) {
-                services.add(p.getServiceInfo());
+            return CredentialProviderInfoFactory.getCredentialProviderServices(
+                    mContext, userId, providerFilter, getEnabledProviders());
+        }
+
+        @Override
+        public List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
+                int providerFilter) {
+            Log.i(TAG, "getCredentialProviderServicesForTesting");
+            verifyGetProvidersPermission();
+
+            final int userId = UserHandle.getCallingUserId();
+            return CredentialProviderInfoFactory.getCredentialProviderServicesForTesting(
+                    mContext, userId, providerFilter, getEnabledProviders());
+        }
+
+        private Set<ServiceInfo> getEnabledProviders() {
+            Set<ServiceInfo> enabledProviders = new HashSet<>();
+            synchronized (mLock) {
+                runForUser(
+                        (service) -> {
+                            enabledProviders.add(service.getCredentialProviderInfo().getServiceInfo());
+                        });
             }
-
-            return services;
+            return enabledProviders;
         }
 
         @Override
@@ -828,11 +815,11 @@
         }
 
         private List<CredentialProviderInfo> getServicesForCredentialDescription(int userId) {
-            return CredentialProviderInfo.getCredentialProviderServices(
+            return CredentialProviderInfoFactory.getCredentialProviderServices(
                     mContext,
                     userId,
-                    /* disableSystemAppVerificationForTests= */ false,
-                    CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS);
+                    CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS,
+                    new HashSet<>());
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
index 546c48f..ee55a1c 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
@@ -21,7 +21,8 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
-import android.service.credentials.CredentialProviderInfo;
+import android.credentials.CredentialProviderInfo;
+import android.service.credentials.CredentialProviderInfoFactory;
 import android.util.Log;
 import android.util.Slog;
 
@@ -82,7 +83,7 @@
             Log.i(TAG, "newServiceInfoLocked with null mInfo , "
                     + serviceComponent.getPackageName());
         }
-        mInfo = new CredentialProviderInfo(
+        mInfo = CredentialProviderInfoFactory.create(
                 getContext(), serviceComponent,
                 mUserId, /*isSystemProvider=*/false);
         return mInfo.getServiceInfo();
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 2c6c0d8..546c37f 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.pm.ServiceInfo;
 import android.credentials.CredentialManager;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.ui.DisabledProviderData;
 import android.credentials.ui.IntentFactory;
 import android.credentials.ui.ProviderData;
@@ -31,11 +32,12 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ResultReceiver;
-import android.service.credentials.CredentialProviderInfo;
+import android.service.credentials.CredentialProviderInfoFactory;
 import android.util.Log;
 import android.util.Slog;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
@@ -118,11 +120,11 @@
                 .map(ProviderData::getProviderFlattenedComponentName)
                 .collect(Collectors.toUnmodifiableSet());
         Set<String> allProviders =
-                CredentialProviderInfo.getCredentialProviderServices(
+                CredentialProviderInfoFactory.getCredentialProviderServices(
                                 mContext,
                                 mUserId,
-                                /* disableSystemAppVerificationForTests= */ false,
-                                CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)
+                                CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY,
+                                new HashSet<>())
                         .stream()
                         .map(CredentialProviderInfo::getServiceInfo)
                         .map(ServiceInfo::getComponentName)
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 13f4b54..8c6e5ce 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -16,12 +16,10 @@
 
 package com.android.server.credentials;
 
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS;
-
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialRequest;
 import android.credentials.GetCredentialResponse;
@@ -31,11 +29,11 @@
 import android.os.CancellationSignal;
 import android.os.RemoteException;
 import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
 import android.util.Log;
 
 import com.android.server.credentials.metrics.ApiName;
 import com.android.server.credentials.metrics.ApiStatus;
+import com.android.server.credentials.metrics.ProviderStatusForMetrics;
 
 import java.util.ArrayList;
 
@@ -95,11 +93,11 @@
         setChosenMetric(componentName);
         if (response != null) {
             mChosenProviderMetric.setChosenProviderStatus(
-                    METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
+                    ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode());
             respondToClientWithResponseAndFinish(response);
         } else {
             mChosenProviderMetric.setChosenProviderStatus(
-                    METRICS_PROVIDER_STATUS_FINAL_FAILURE);
+                    ProviderStatusForMetrics.FINAL_FAILURE.getMetricCode());
             respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL,
                     "Invalid response from provider");
         }
@@ -120,18 +118,18 @@
         }
         if (isSessionCancelled()) {
             logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+                    ApiStatus.CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
             return;
         }
         try {
             mClientCallback.onResponse(response);
             logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_SUCCESS);
+                    ApiStatus.SUCCESS);
         } catch (RemoteException e) {
             Log.i(TAG, "Issue while responding to client with a response : " + e.getMessage());
             logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_FAILURE);
+                    ApiStatus.FAILURE);
         }
         finishSession(/*propagateCancellation=*/false);
     }
@@ -143,7 +141,7 @@
         }
         if (isSessionCancelled()) {
             logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
-                    ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+                    ApiStatus.CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
             return;
         }
@@ -160,10 +158,10 @@
     private void logFailureOrUserCancel(String errorType) {
         if (GetCredentialException.TYPE_USER_CANCELED.equals(errorType)) {
             logApiCall(ApiName.GET_CREDENTIAL,
-                    /* apiStatus */ ApiStatus.METRICS_API_STATUS_USER_CANCELED);
+                    /* apiStatus */ ApiStatus.USER_CANCELED);
         } else {
             logApiCall(ApiName.GET_CREDENTIAL,
-                    /* apiStatus */ ApiStatus.METRICS_API_STATUS_FAILURE);
+                    /* apiStatus */ ApiStatus.FAILURE);
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index f75a9b6..91470f6 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -16,12 +16,6 @@
 
 package com.android.server.credentials;
 
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -43,20 +37,8 @@
 
     private static final String TAG = "MetricUtilities";
 
-    private static final int DEFAULT_INT_32 = -1;
-    private static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
-
-    // Metrics constants TODO(b/269290341) migrate to enums eventually to improve
-    protected static final int METRICS_PROVIDER_STATUS_FINAL_FAILURE =
-            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
-    protected static final int METRICS_PROVIDER_STATUS_QUERY_FAILURE =
-            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
-    protected static final int METRICS_PROVIDER_STATUS_FINAL_SUCCESS =
-            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
-    protected static final int METRICS_PROVIDER_STATUS_QUERY_SUCCESS =
-            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS;
-    protected static final int METRICS_PROVIDER_STATUS_UNKNOWN =
-            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN;
+    public static final int DEFAULT_INT_32 = -1;
+    public static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
 
 
     /**
@@ -80,6 +62,21 @@
     }
 
     /**
+     * Given any two timestamps in nanoseconds, this gets the difference and converts to
+     * milliseconds. Assumes the difference is not larger than the maximum int size.
+     *
+     * @param t2 the final timestamp
+     * @param t1 the initial timestamp
+     * @return the timestamp difference converted to microseconds
+     */
+    protected static int getMetricTimestampDifferenceMicroseconds(long t2, long t1) {
+        if (t2 - t1 > Integer.MAX_VALUE) {
+            throw new ArithmeticException("Input timestamps are too far apart and unsupported");
+        }
+        return (int) ((t2 - t1) / 1000);
+    }
+
+    /**
      * The most common logging helper, handles the overall status of the API request with the
      * provider status and latencies. Other versions of this method may be more useful depending
      * on the situation, as this is geared towards the logging of {@link ProviderSession} types.
@@ -102,7 +99,7 @@
         for (var session : providerSessions) {
             CandidateProviderMetric metric = session.mCandidateProviderMetric;
             candidateUidList[index] = metric.getCandidateUid();
-            candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMs();
+            candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMicroseconds();
             candidateStatusList[index] = metric.getProviderQueryStatus();
             index++;
         }
@@ -116,9 +113,9 @@
                 /* repeated_candidate_provider_status */ candidateStatusList,
                 /* chosen_provider_uid */ chosenProviderMetric.getChosenUid(),
                 /* chosen_provider_round_trip_time_overall_microseconds */
-                chosenProviderMetric.getEntireProviderLatencyMs(),
-                /* chosen_provider_final_phase_microseconds */
-                chosenProviderMetric.getFinalPhaseLatencyMs(),
+                chosenProviderMetric.getEntireProviderLatencyMicroseconds(),
+                /* chosen_provider_final_phase_microseconds (backwards compat only) */
+                DEFAULT_INT_32,
                 /* chosen_provider_status */ chosenProviderMetric.getChosenProviderStatus());
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index ce9fca7..b7a4cd5 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -20,11 +20,11 @@
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.credentials.ClearCredentialStateException;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.ui.ProviderData;
 import android.credentials.ui.ProviderPendingIntentResponse;
 import android.service.credentials.CallingAppInfo;
 import android.service.credentials.ClearCredentialStateRequest;
-import android.service.credentials.CredentialProviderInfo;
 import android.util.Log;
 import android.util.Slog;
 
@@ -119,8 +119,8 @@
     @Override
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
+            mCandidateProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime());
             mRemoteCredentialService.onClearCredentialState(mProviderRequest, this);
-            mCandidateProviderMetric.setStartTimeNanoseconds(System.nanoTime());
         }
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 370d21f..640cc33 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.credentials.CreateCredentialException;
 import android.credentials.CreateCredentialResponse;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.ui.CreateCredentialProviderData;
 import android.credentials.ui.Entry;
 import android.credentials.ui.ProviderPendingIntentResponse;
@@ -33,7 +34,6 @@
 import android.service.credentials.CallingAppInfo;
 import android.service.credentials.CreateCredentialRequest;
 import android.service.credentials.CreateEntry;
-import android.service.credentials.CredentialProviderInfo;
 import android.service.credentials.CredentialProviderService;
 import android.service.credentials.RemoteEntry;
 import android.util.Log;
@@ -226,8 +226,8 @@
     @Override
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
+            mCandidateProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime());
             mRemoteCredentialService.onCreateCredential(mProviderRequest, this);
-            mCandidateProviderMetric.setStartTimeNanoseconds(System.nanoTime());
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 80a61b9..07e2f87 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.credentials.CredentialOption;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialResponse;
 import android.credentials.ui.AuthenticationEntry;
@@ -35,7 +36,6 @@
 import android.service.credentials.BeginGetCredentialResponse;
 import android.service.credentials.CallingAppInfo;
 import android.service.credentials.CredentialEntry;
-import android.service.credentials.CredentialProviderInfo;
 import android.service.credentials.CredentialProviderService;
 import android.service.credentials.GetCredentialRequest;
 import android.service.credentials.RemoteEntry;
@@ -268,8 +268,8 @@
     @Override
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
+            mCandidateProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime());
             mRemoteCredentialService.onBeginGetCredential(mProviderRequest, this);
-            mCandidateProviderMetric.setStartTimeNanoseconds(System.nanoTime());
         }
     }
 
@@ -502,8 +502,7 @@
             String id = generateUniqueId();
             Entry entry = new Entry(CREDENTIAL_ENTRY_KEY,
                     id, credentialEntry.getSlice(),
-                    setUpFillInIntent(credentialEntry
-                            .getBeginGetCredentialOption().getId()));
+                    setUpFillInIntent(credentialEntry.getBeginGetCredentialOptionId()));
             mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry));
         }
 
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 53ed070..a857695 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -16,9 +16,6 @@
 
 package com.android.server.credentials;
 
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_FAILURE;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_SUCCESS;
-
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,14 +24,15 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.credentials.Credential;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.ui.ProviderData;
 import android.credentials.ui.ProviderPendingIntentResponse;
 import android.os.ICancellationSignal;
 import android.os.RemoteException;
-import android.service.credentials.CredentialProviderInfo;
 import android.util.Log;
 
 import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.ProviderStatusForMetrics;
 
 import java.util.UUID;
 
@@ -205,9 +203,11 @@
         mCandidateProviderMetric
                 .setQueryFinishTimeNanoseconds(System.nanoTime());
         if (isTerminatingStatus(status)) {
-            mCandidateProviderMetric.setProviderQueryStatus(METRICS_PROVIDER_STATUS_QUERY_FAILURE);
+            mCandidateProviderMetric.setProviderQueryStatus(ProviderStatusForMetrics.QUERY_FAILURE
+                    .getMetricCode());
         } else if (isCompletionStatus(status)) {
-            mCandidateProviderMetric.setProviderQueryStatus(METRICS_PROVIDER_STATUS_QUERY_SUCCESS);
+            mCandidateProviderMetric.setProviderQueryStatus(ProviderStatusForMetrics.QUERY_SUCCESS
+                    .getMetricCode());
         }
     }
 
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 86e05cf..c1f35d0 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -22,6 +22,7 @@
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
+import android.credentials.CredentialProviderInfo;
 import android.credentials.ui.ProviderData;
 import android.credentials.ui.UserSelectionDialogResult;
 import android.os.Binder;
@@ -30,7 +31,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
 import android.util.Log;
 
 import com.android.internal.R;
@@ -78,7 +78,8 @@
     //TODO improve design to allow grouped metrics per request
     protected final String mHybridService;
 
-    @NonNull protected RequestSessionStatus mRequestSessionStatus =
+    @NonNull
+    protected RequestSessionStatus mRequestSessionStatus =
             RequestSessionStatus.IN_PROGRESS;
 
     /** The status in which a given request session is. */
@@ -213,6 +214,7 @@
 
     /**
      * Called by RequestSession's upon chosen metric determination.
+     *
      * @param componentName the componentName to associate with a provider
      */
     protected void setChosenMetric(ComponentName componentName) {
@@ -220,8 +222,8 @@
                 .mCandidateProviderMetric;
         mChosenProviderMetric.setChosenUid(metric.getCandidateUid());
         mChosenProviderMetric.setFinalFinishTimeNanoseconds(System.nanoTime());
-        mChosenProviderMetric.setQueryFinishTimeNanoseconds(
-                metric.getQueryFinishTimeNanoseconds());
-        mChosenProviderMetric.setStartTimeNanoseconds(metric.getStartTimeNanoseconds());
+        mChosenProviderMetric.setQueryPhaseLatencyMicroseconds(
+                metric.getQueryLatencyMicroseconds());
+        mChosenProviderMetric.setQueryStartTimeNanoseconds(metric.getStartQueryTimeNanoseconds());
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
index 36a1f2d..22cab70 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
@@ -22,11 +22,11 @@
 import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED;
 
 public enum ApiStatus {
-    METRICS_API_STATUS_SUCCESS(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS),
-    METRICS_API_STATUS_FAILURE(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE),
-    METRICS_API_STATUS_CLIENT_CANCELED(
+    SUCCESS(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS),
+    FAILURE(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE),
+    CLIENT_CANCELED(
             CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED),
-    METRICS_API_STATUS_USER_CANCELED(
+    USER_CANCELED(
             CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED);
 
     private final int mInnerMetricCode;
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
index acfb4a4..9f438ec 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
@@ -18,63 +18,72 @@
 
 /**
  * The central candidate provider metric object that mimics our defined metric setup.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
+ * TODO(b/270403549) - iterate on this in V3+
  */
 public class CandidateProviderMetric {
 
+    private static final String TAG = "CandidateProviderMetric";
     private int mCandidateUid = -1;
-    private long mStartTimeNanoseconds = -1;
+
+    // Raw timestamp in nanoseconds, will be converted to microseconds for logging
+
+    private long mStartQueryTimeNanoseconds = -1;
     private long mQueryFinishTimeNanoseconds = -1;
 
     private int mProviderQueryStatus = -1;
 
-    public CandidateProviderMetric(long startTime, long queryFinishTime, int providerQueryStatus,
-            int candidateUid) {
-        this.mStartTimeNanoseconds = startTime;
-        this.mQueryFinishTimeNanoseconds = queryFinishTime;
-        this.mProviderQueryStatus = providerQueryStatus;
-        this.mCandidateUid = candidateUid;
+    public CandidateProviderMetric() {
     }
 
-    public CandidateProviderMetric(){}
+    /* ---------- Latencies ---------- */
 
-    public void setStartTimeNanoseconds(long startTimeNanoseconds) {
-        this.mStartTimeNanoseconds = startTimeNanoseconds;
+    public void setStartQueryTimeNanoseconds(long startQueryTimeNanoseconds) {
+        this.mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
     }
 
     public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
         this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
     }
 
-    public void setProviderQueryStatus(int providerQueryStatus) {
-        this.mProviderQueryStatus = providerQueryStatus;
-    }
-
-    public void setCandidateUid(int candidateUid) {
-        this.mCandidateUid = candidateUid;
-    }
-
-    public long getStartTimeNanoseconds() {
-        return this.mStartTimeNanoseconds;
+    public long getStartQueryTimeNanoseconds() {
+        return this.mStartQueryTimeNanoseconds;
     }
 
     public long getQueryFinishTimeNanoseconds() {
         return this.mQueryFinishTimeNanoseconds;
     }
 
+    /**
+     * Returns the latency in microseconds for the query phase.
+     */
+    public int getQueryLatencyMicroseconds() {
+        return (int) ((this.getQueryFinishTimeNanoseconds()
+                - this.getStartQueryTimeNanoseconds()) / 1000);
+    }
+
+    // TODO (in direct next dependent CL, so this is transient) - add reference timestamp in micro
+    // seconds for this too.
+
+    /* ------------- Provider Query Status ------------ */
+
+    public void setProviderQueryStatus(int providerQueryStatus) {
+        this.mProviderQueryStatus = providerQueryStatus;
+    }
+
     public int getProviderQueryStatus() {
         return this.mProviderQueryStatus;
     }
 
+    /* -------------- Candidate Uid ---------------- */
+
+    public void setCandidateUid(int candidateUid) {
+        this.mCandidateUid = candidateUid;
+    }
+
     public int getCandidateUid() {
         return this.mCandidateUid;
     }
-
-    /**
-     * Returns the latency in microseconds for the query phase.
-     */
-    public int getQueryLatencyMs() {
-        return (int) ((this.getQueryFinishTimeNanoseconds()
-                - this.getStartTimeNanoseconds()) / 1000);
-    }
-
 }
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
index c4d0b3c..0310255 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
@@ -16,18 +16,42 @@
 
 package com.android.server.credentials.metrics;
 
+import android.util.Log;
+
+import com.android.server.credentials.MetricUtilities;
+
 /**
  * The central chosen provider metric object that mimics our defined metric setup.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
+ * TODO(b/270403549) - iterate on this in V3+
  */
 public class ChosenProviderMetric {
 
+    // TODO(b/270403549) - applies elsewhere, likely removed or replaced with a count-index (1,2,3)
+    private static final String TAG = "ChosenProviderMetric";
     private int mChosenUid = -1;
-    private long mStartTimeNanoseconds = -1;
-    private long mQueryFinishTimeNanoseconds = -1;
+
+    // Latency figures typically fed in from prior CandidateProviderMetric
+
+    private int mPreQueryPhaseLatencyMicroseconds = -1;
+    private int mQueryPhaseLatencyMicroseconds = -1;
+
+    // Timestamps kept in raw nanoseconds. Expected to be converted to microseconds from using
+    // reference 'mServiceBeganTimeNanoseconds' during metric log point.
+
+    private long mServiceBeganTimeNanoseconds = -1;
+    private long mQueryStartTimeNanoseconds = -1;
+    private long mUiCallStartTimeNanoseconds = -1;
+    private long mUiCallEndTimeNanoseconds = -1;
     private long mFinalFinishTimeNanoseconds = -1;
     private int mChosenProviderStatus = -1;
 
-    public ChosenProviderMetric() {}
+    public ChosenProviderMetric() {
+    }
+
+    /* ------------------- UID ------------------- */
 
     public int getChosenUid() {
         return mChosenUid;
@@ -37,30 +61,138 @@
         mChosenUid = chosenUid;
     }
 
-    public long getStartTimeNanoseconds() {
-        return mStartTimeNanoseconds;
+    /* ---------------- Latencies ------------------ */
+
+
+    /* ----- Direct Latencies ------- */
+
+    /**
+     * In order for a chosen provider to be selected, the call must have successfully begun.
+     * Thus, the {@link InitialPhaseMetric} can directly pass this initial latency figure into
+     * this chosen provider metric.
+     *
+     * @param preQueryPhaseLatencyMicroseconds the millisecond latency for the service start,
+     *                                         typically passed in through the
+     *                                         {@link InitialPhaseMetric}
+     */
+    public void setPreQueryPhaseLatencyMicroseconds(int preQueryPhaseLatencyMicroseconds) {
+        mPreQueryPhaseLatencyMicroseconds = preQueryPhaseLatencyMicroseconds;
     }
 
-    public void setStartTimeNanoseconds(long startTimeNanoseconds) {
-        mStartTimeNanoseconds = startTimeNanoseconds;
+    /**
+     * In order for a chosen provider to be selected, a candidate provider must exist. The
+     * candidate provider can directly pass the final latency figure into this chosen provider
+     * metric.
+     *
+     * @param queryPhaseLatencyMicroseconds the millisecond latency for the query phase, typically
+     *                                      passed in through the {@link CandidateProviderMetric}
+     */
+    public void setQueryPhaseLatencyMicroseconds(int queryPhaseLatencyMicroseconds) {
+        mQueryPhaseLatencyMicroseconds = queryPhaseLatencyMicroseconds;
     }
 
-    public long getQueryFinishTimeNanoseconds() {
-        return mQueryFinishTimeNanoseconds;
+    public int getPreQueryPhaseLatencyMicroseconds() {
+        return mPreQueryPhaseLatencyMicroseconds;
     }
 
-    public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
-        mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
+    public int getQueryPhaseLatencyMicroseconds() {
+        return mQueryPhaseLatencyMicroseconds;
+    }
+
+    public int getUiPhaseLatencyMicroseconds() {
+        return (int) ((this.mUiCallEndTimeNanoseconds
+                - this.mUiCallStartTimeNanoseconds) / 1000);
+    }
+
+    /**
+     * Returns the full provider (invocation to response) latency in microseconds. Expects the
+     * start time to be provided, such as from {@link CandidateProviderMetric}.
+     */
+    public int getEntireProviderLatencyMicroseconds() {
+        return (int) ((this.mFinalFinishTimeNanoseconds
+                - this.mQueryStartTimeNanoseconds) / 1000);
+    }
+
+    /**
+     * Returns the full (platform invoked to response) latency in microseconds. Expects the
+     * start time to be provided, such as from {@link InitialPhaseMetric}.
+     */
+    public int getEntireLatencyMicroseconds() {
+        return (int) ((this.mFinalFinishTimeNanoseconds
+                - this.mServiceBeganTimeNanoseconds) / 1000);
+    }
+
+    /* ----- Timestamps for Latency ----- */
+
+    /**
+     * In order for a chosen provider to be selected, the call must have successfully begun.
+     * Thus, the {@link InitialPhaseMetric} can directly pass this initial timestamp into this
+     * chosen provider metric.
+     *
+     * @param serviceBeganTimeNanoseconds the timestamp moment when the platform was called,
+     *                                    typically passed in through the {@link InitialPhaseMetric}
+     */
+    public void setServiceBeganTimeNanoseconds(long serviceBeganTimeNanoseconds) {
+        mServiceBeganTimeNanoseconds = serviceBeganTimeNanoseconds;
+    }
+
+    public void setQueryStartTimeNanoseconds(long queryStartTimeNanoseconds) {
+        mQueryStartTimeNanoseconds = queryStartTimeNanoseconds;
+    }
+
+    public void setUiCallStartTimeNanoseconds(long uiCallStartTimeNanoseconds) {
+        this.mUiCallStartTimeNanoseconds = uiCallStartTimeNanoseconds;
+    }
+
+    public void setUiCallEndTimeNanoseconds(long uiCallEndTimeNanoseconds) {
+        this.mUiCallEndTimeNanoseconds = uiCallEndTimeNanoseconds;
+    }
+
+    public void setFinalFinishTimeNanoseconds(long finalFinishTimeNanoseconds) {
+        mFinalFinishTimeNanoseconds = finalFinishTimeNanoseconds;
+    }
+
+    public long getServiceBeganTimeNanoseconds() {
+        return mServiceBeganTimeNanoseconds;
+    }
+
+    public long getQueryStartTimeNanoseconds() {
+        return mQueryStartTimeNanoseconds;
+    }
+
+    public long getUiCallStartTimeNanoseconds() {
+        return mUiCallStartTimeNanoseconds;
+    }
+
+    public long getUiCallEndTimeNanoseconds() {
+        return mUiCallEndTimeNanoseconds;
     }
 
     public long getFinalFinishTimeNanoseconds() {
         return mFinalFinishTimeNanoseconds;
     }
 
-    public void setFinalFinishTimeNanoseconds(long finalFinishTimeNanoseconds) {
-        mFinalFinishTimeNanoseconds = finalFinishTimeNanoseconds;
+    /* --- Time Stamp Conversion to Microseconds --- */
+
+    /**
+     * We collect raw timestamps in nanoseconds for ease of collection. However, given the scope
+     * of our logging timeframe, and size considerations of the metric, we require these to give us
+     * the microsecond timestamps from the start reference point.
+     *
+     * @param specificTimestamp the timestamp to consider, must be greater than the reference
+     * @return the microsecond integer timestamp from service start to query began
+     */
+    public int getTimestampFromReferenceStartMicroseconds(long specificTimestamp) {
+        if (specificTimestamp < this.mServiceBeganTimeNanoseconds) {
+            Log.i(TAG, "The timestamp is before service started, falling back to default int");
+            return MetricUtilities.DEFAULT_INT_32;
+        }
+        return (int) ((specificTimestamp
+                - this.mServiceBeganTimeNanoseconds) / 1000);
     }
 
+    /* ----------- Provider Status -------------- */
+
     public int getChosenProviderStatus() {
         return mChosenProviderStatus;
     }
@@ -68,23 +200,4 @@
     public void setChosenProviderStatus(int chosenProviderStatus) {
         mChosenProviderStatus = chosenProviderStatus;
     }
-
-    /**
-     * Returns the full provider (invocation to response) latency in microseconds.
-     */
-    public int getEntireProviderLatencyMs() {
-        return (int) ((this.getFinalFinishTimeNanoseconds()
-                - this.getStartTimeNanoseconds()) / 1000);
-    }
-
-    // TODO get post click final phase and re-add the query phase time to metric
-
-    /**
-     * Returns the end of query to response phase latency in microseconds.
-     */
-    public int getFinalPhaseLatencyMs() {
-        return (int) ((this.getFinalFinishTimeNanoseconds()
-                - this.getQueryFinishTimeNanoseconds()) / 1000);
-    }
-
 }
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
new file mode 100644
index 0000000..5f062b0
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -0,0 +1,130 @@
+/*
+ * 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.credentials.metrics;
+
+/**
+ * This handles metrics collected prior to any remote calls to providers.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
+ * TODO(b/270403549) - iterate on this in V3+
+ */
+public class InitialPhaseMetric {
+    private static final String TAG = "PreCandidateMetric";
+
+    // The api being called, default set to unknown
+    private int mApiName = ApiName.UNKNOWN.getMetricCode();
+    // The caller uid of the calling application, default to -1
+    private int mCallerUid = -1;
+    // The session id to unite multiple atom emits, default to -1
+    private long mSessionId = -1;
+    // A sequence id to order united emits, default to -1
+    private int mSequenceId = -1;
+    private int mCountRequestClassType = -1;
+
+    // Raw timestamps in nanoseconds, *the only* one logged as such (i.e. 64 bits) since it is a
+    // reference point.
+    private long mCredentialServiceStartedTimeNanoseconds = -1;
+
+    // A reference point to give this object utility to capture latency. Can be directly handed
+    // over to the next latency object.
+    private long mCredentialServiceBeginQueryTimeNanoseconds = -1;
+
+
+    public InitialPhaseMetric() {
+    }
+
+    /* ---------- Latencies ---------- */
+
+    /* -- Direct Latencies -- */
+
+    public int getServiceStartToQueryLatencyMicroseconds() {
+        return (int) ((this.mCredentialServiceStartedTimeNanoseconds
+                - this.mCredentialServiceBeginQueryTimeNanoseconds) / 1000);
+    }
+
+    /* -- Timestamps -- */
+
+    public void setCredentialServiceStartedTimeNanoseconds(
+            long credentialServiceStartedTimeNanoseconds
+    ) {
+        this.mCredentialServiceStartedTimeNanoseconds = credentialServiceStartedTimeNanoseconds;
+    }
+
+    public void setCredentialServiceBeginQueryTimeNanoseconds(
+            long credentialServiceBeginQueryTimeNanoseconds) {
+        mCredentialServiceBeginQueryTimeNanoseconds = credentialServiceBeginQueryTimeNanoseconds;
+    }
+
+    public long getCredentialServiceStartedTimeNanoseconds() {
+        return mCredentialServiceStartedTimeNanoseconds;
+    }
+
+    public long getCredentialServiceBeginQueryTimeNanoseconds() {
+        return mCredentialServiceBeginQueryTimeNanoseconds;
+    }
+
+    /* ------ ApiName ------ */
+
+    public void setApiName(int apiName) {
+        mApiName = apiName;
+    }
+
+    public int getApiName() {
+        return mApiName;
+    }
+
+    /* ------ CallerUid ------ */
+
+    public void setCallerUid(int callerUid) {
+        mCallerUid = callerUid;
+    }
+
+    public int getCallerUid() {
+        return mCallerUid;
+    }
+
+    /* ------ SessionId ------ */
+
+    public void setSessionId(long sessionId) {
+        mSessionId = sessionId;
+    }
+
+    public long getSessionId() {
+        return mSessionId;
+    }
+
+    /* ------ SequenceId ------ */
+
+    public void setSequenceId(int sequenceId) {
+        mSequenceId = sequenceId;
+    }
+
+    public int getSequenceId() {
+        return mSequenceId;
+    }
+
+    /* ------ Count Request Class Types ------ */
+
+    public void setCountRequestClassType(int countRequestClassType) {
+        mCountRequestClassType = countRequestClassType;
+    }
+
+    public int getCountRequestClassType() {
+        return mCountRequestClassType;
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java b/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java
new file mode 100644
index 0000000..08f1afa
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java
@@ -0,0 +1,52 @@
+/*
+ * 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.credentials.metrics;
+
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN;
+
+public enum ProviderStatusForMetrics {
+
+    UNKNOWN(
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN),
+    FINAL_FAILURE(
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE),
+    QUERY_FAILURE(
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE),
+    FINAL_SUCCESS(
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS),
+    QUERY_SUCCESS(
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS);
+
+    private final int mInnerMetricCode;
+
+    ProviderStatusForMetrics(int innerMetricCode) {
+        this.mInnerMetricCode = innerMetricCode;
+    }
+
+    /**
+     * Gives the West-world version of the metric name.
+     *
+     * @return a code corresponding to the west world metric name
+     */
+    public int getMetricCode() {
+        return this.mInnerMetricCode;
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 4351bc1..80100a9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -24,6 +24,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -52,6 +54,11 @@
     @GuardedBy("mLock")
     private final SparseIntArray mPermissionPolicy = new SparseIntArray();
 
+    @GuardedBy("mLock")
+    private List<String> mLauncherShortcutOverrides =
+            new ArrayList<>();
+
+
     /** Maps to {@code ActiveAdmin.mAdminCanGrantSensorsPermissions}. */
     private final AtomicBoolean mCanGrantSensorsPermissions = new AtomicBoolean(false);
 
@@ -122,6 +129,22 @@
         mCanGrantSensorsPermissions.set(canGrant);
     }
 
+    @Override
+    public List<String> getLauncherShortcutOverrides() {
+        synchronized (mLock) {
+            return new ArrayList<>(mLauncherShortcutOverrides);
+        }
+    }
+
+    /**
+     * Sets a list of packages for which shortcuts should be replaced by their badged version.
+     */
+    public void setLauncherShortcutOverrides(List<String> launcherShortcutOverrides) {
+        synchronized (mLock) {
+            mLauncherShortcutOverrides = new ArrayList<>(launcherShortcutOverrides);
+        }
+    }
+
     /** Dump content */
     public void dump(IndentingPrintWriter pw) {
         synchronized (mLock) {
@@ -131,6 +154,8 @@
             pw.println("Password quality: " + mPasswordQuality);
             pw.println("Permission policy: " + mPermissionPolicy);
             pw.println("Admin can grant sensors permission: " + mCanGrantSensorsPermissions.get());
+            pw.print("Shortcuts overrides: ");
+            pw.println(mLauncherShortcutOverrides);
             pw.decreaseIndent();
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 400ee1d..a4e563b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3522,16 +3522,30 @@
                 userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
         updatePermissionPolicyCache(userId);
         updateAdminCanGrantSensorsPermissionCache(userId);
-
         final List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs;
+        boolean isManagedSubscription;
+
         synchronized (getLockObject()) {
             ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
             preferentialNetworkServiceConfigs = owner != null
                     ? owner.mPreferentialNetworkServiceConfigs
                     : List.of(PreferentialNetworkServiceConfig.DEFAULT);
+
+            isManagedSubscription = owner != null && owner.mManagedSubscriptionsPolicy != null
+                    && owner.mManagedSubscriptionsPolicy.getPolicyType()
+                    == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS;
         }
         updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfigs);
 
+        if (isManagedSubscription) {
+            String defaultDialerPackageName = getDefaultRoleHolderPackageName(
+                    com.android.internal.R.string.config_defaultDialer);
+            String defaultSmsPackageName = getDefaultRoleHolderPackageName(
+                    com.android.internal.R.string.config_defaultSms);
+            updateDialerAndSmsManagedShortcutsOverrideCache(defaultDialerPackageName,
+                    defaultSmsPackageName);
+        }
+
         startOwnerService(userId, "start-user");
         if (isDevicePolicyEngineEnabled()) {
             mDevicePolicyEngine.handleStartUser(userId);
@@ -7614,6 +7628,7 @@
 
         if (isWorkProfileTelephonyFlagEnabled()) {
             clearManagedSubscriptionsPolicy();
+            clearLauncherShortcutOverrides();
             updateTelephonyCrossProfileIntentFilters(parentId, UserHandle.USER_NULL, false);
         }
         Slogf.i(LOG_TAG, "Cleaning up device-wide policies done.");
@@ -7631,6 +7646,10 @@
         }
     }
 
+    private void clearLauncherShortcutOverrides() {
+        mPolicyCache.setLauncherShortcutOverrides(new ArrayList<>());
+    }
+
     private void updateTelephonyCrossProfileIntentFilters(int parentUserId, int profileUserId,
             boolean enableWorkTelephony) {
         try {
@@ -22755,12 +22774,30 @@
             } else {
                 Slogf.w(LOG_TAG, "Couldn't install sms app, sms app package is null");
             }
+
+            updateDialerAndSmsManagedShortcutsOverrideCache(defaultDialerPackageName,
+                    defaultSmsPackageName);
         } catch (RemoteException re) {
             // shouldn't happen
             Slogf.wtf(LOG_TAG, "Failed to install dialer/sms app", re);
         }
     }
 
+    private void updateDialerAndSmsManagedShortcutsOverrideCache(
+            String defaultDialerPackageName, String defaultSmsPackageName) {
+
+        List<String> shortcutOverrides = new ArrayList<>();
+
+        if (defaultDialerPackageName != null) {
+            shortcutOverrides.add(defaultDialerPackageName);
+        }
+
+        if (defaultSmsPackageName != null) {
+            shortcutOverrides.add(defaultSmsPackageName);
+        }
+        mPolicyCache.setLauncherShortcutOverrides(shortcutOverrides);
+    }
+
     private void registerListenerToAssignSubscriptionsToUser(int userId) {
         synchronized (mSubscriptionsChangedListenerLock) {
             if (mSubscriptionsChangedListener != null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 3ca158d..194647fd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -120,7 +120,9 @@
                 } else {
                     mDeviceStateCache.setDeviceOwnerType(NO_DEVICE_OWNER);
                 }
-
+                for (int userId : usersIds) {
+                    mDeviceStateCache.setHasProfileOwner(userId, hasProfileOwner(userId));
+                }
             } else {
                 mUserManagerInternal.setDeviceManaged(hasDeviceOwner());
                 for (int userId : usersIds) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
index cd5ac7bc..1731590 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
@@ -782,6 +782,34 @@
         assertTrue(mIntf.isUidInForeground(UID));
     }
 
+    @Test
+    public void testAppWidgetVisibleDoesntChangeUidState() {
+        procStateBuilder(UID)
+                .topState()
+                .update();
+
+        SparseArray<String> updatedAppWidgetVisibilities = new SparseArray<>();
+        updatedAppWidgetVisibilities.put(UID, "");
+
+        mIntf.updateAppWidgetVisibility(updatedAppWidgetVisibilities, true);
+
+        assertEquals(UID_STATE_TOP, mIntf.getUidState(UID));
+    }
+
+    @Test
+    public void testAppWidgetNotVisibleDoesntChangeUidState() {
+        SparseArray<String> updatedAppWidgetVisibilities = new SparseArray<>();
+        updatedAppWidgetVisibilities.put(UID, "");
+        mIntf.updateAppWidgetVisibility(updatedAppWidgetVisibilities, true);
+        procStateBuilder(UID)
+                .topState()
+                .update();
+
+        mIntf.updateAppWidgetVisibility(updatedAppWidgetVisibilities, false);
+
+        assertEquals(UID_STATE_TOP, mIntf.getUidState(UID));
+    }
+
     public void testUidStateChangedCallback(int initialState, int finalState) {
         int initialUidState = processStateToUidState(initialState);
         int finalUidState = processStateToUidState(finalState);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 7149265..6861c2f 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -108,6 +108,7 @@
     <uses-permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES" />
     <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB" />
     <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
+    <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
 
     <queries>
         <package android:name="com.android.servicestests.apps.suspendtestapp" />
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 85a2446..375b52d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -21,6 +21,7 @@
 import static android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_WINDOWED;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.content.ComponentName;
 import android.os.IpcDataCache;
@@ -43,6 +44,7 @@
 @RunWith(AndroidJUnit4.class)
 public class OwnersTest extends DpmTestBase {
 
+    private static final int TEST_PO_USER = 10;
     private static final String TESTDPC_PACKAGE = "com.afwsamples.testdpc";
     private final DeviceStateCacheImpl mDeviceStateCache = new DeviceStateCacheImpl();
 
@@ -55,11 +57,11 @@
 
     @Test
     public void loadProfileOwner() throws Exception {
-        getServices().addUsers(10);
+        getServices().addUsers(TEST_PO_USER);
 
         final Owners owners = makeOwners();
 
-        DpmTestUtils.writeToFile(owners.getProfileOwnerFile(10),
+        DpmTestUtils.writeToFile(owners.getProfileOwnerFile(TEST_PO_USER),
                 DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/profile_owner_1.xml"));
 
         owners.load();
@@ -71,6 +73,9 @@
         assertThat(owners.getProfileOwnerComponent(10))
                 .isEqualTo(new ComponentName(TESTDPC_PACKAGE,
                         "com.afwsamples.testdpc.DeviceAdminReceiver"));
+
+        assertWithMessage("Profile owner data in DeviceStateCache wasn't populated")
+                .that(mDeviceStateCache.isUserOrganizationManaged(TEST_PO_USER)).isTrue();
     }
 
     @Test
@@ -90,6 +95,10 @@
                         "com.afwsamples.testdpc.DeviceAdminReceiver"));
 
         assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(TYPE_INSTALL_WINDOWED);
+
+        assertWithMessage("Device owner data in DeviceStateCache wasn't populated")
+                .that(mDeviceStateCache.isUserOrganizationManaged(owners.getDeviceOwnerUserId()))
+                .isTrue();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index de1c219..94d30bb 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -24,6 +24,8 @@
 
 import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -32,8 +34,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -61,10 +65,13 @@
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.VirtualDisplayConfig;
+import android.media.projection.IMediaProjectionManager;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.MessageQueue;
 import android.os.Process;
+import android.view.ContentRecordingSession;
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayEventReceiver;
@@ -158,25 +165,40 @@
                 }
             };
 
-   class BasicInjector extends DisplayManagerService.Injector {
-       @Override
-       VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
-               Handler handler, DisplayAdapter.Listener displayAdapterListener) {
-           return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
-                   (String name, boolean secure, float refreshRate) -> mMockDisplayToken);
-       }
+    class BasicInjector extends DisplayManagerService.Injector {
+        @Override
+        IMediaProjectionManager getProjectionService() {
+            return mMockProjectionService;
+        }
 
-       @Override
-       LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
-               Handler handler, DisplayAdapter.Listener displayAdapterListener) {
-           return new LocalDisplayAdapter(syncRoot, context, handler,
-                   displayAdapterListener, new LocalDisplayAdapter.Injector() {
-               @Override
-               public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
-                   return mSurfaceControlProxy;
-               }
-           });
-       }
+        @Override
+        VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
+                Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+            return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+                    new VirtualDisplayAdapter.SurfaceControlDisplayFactory() {
+                        @Override
+                        public IBinder createDisplay(String name, boolean secure,
+                                float requestedRefreshRate) {
+                            return mMockDisplayToken;
+                        }
+
+                        @Override
+                        public void destroyDisplay(IBinder displayToken) {
+                        }
+                    });
+        }
+
+        @Override
+        LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
+                Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+            return new LocalDisplayAdapter(syncRoot, context, handler,
+                    displayAdapterListener, new LocalDisplayAdapter.Injector() {
+                        @Override
+                        public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
+                            return mSurfaceControlProxy;
+                        }
+                    });
+        }
 
         @Override
         int setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
@@ -198,6 +220,7 @@
 
     private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
 
+    @Mock IMediaProjectionManager mMockProjectionService;
     @Mock IVirtualDeviceManager mIVirtualDeviceManager;
     @Mock InputManagerInternal mMockInputManagerInternal;
     @Mock VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal;
@@ -285,6 +308,7 @@
         builder.setFlags(flags);
         int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
                 null /* projection */, PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
 
@@ -410,6 +434,7 @@
         builder.setUniqueId(uniqueId);
         int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
                 null /* projection */, PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
 
@@ -446,6 +471,7 @@
         builder.setUniqueId(uniqueId);
         int displayId = bs.createVirtualDisplay(builder.build(), /* callback= */ mMockAppToken,
                 /* projection= */ null, PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
 
@@ -479,6 +505,7 @@
         builder.setUniqueId(uniqueId);
         int displayId = bs.createVirtualDisplay(builder.build(), /* callback= */ mMockAppToken,
                 /* projection= */ null, PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
 
@@ -720,6 +747,7 @@
         builder.setUniqueId(uniqueId);
         final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
                 mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         // The second virtual display requests to mirror the first virtual display.
         final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
@@ -731,6 +759,7 @@
         final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
                 mMockAppToken2 /* callback */, null /* projection */,
                 PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
 
         // flush the handler
@@ -768,6 +797,7 @@
                         virtualDevice /* virtualDeviceToken */,
                         mock(DisplayWindowPolicyController.class),
                         PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
         int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId;
 
         // Create a second virtual display. This should be added to the previously created display
@@ -783,6 +813,7 @@
                         virtualDevice /* virtualDeviceToken */,
                         mock(DisplayWindowPolicyController.class),
                         PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
         int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId;
 
         assertEquals(
@@ -820,6 +851,7 @@
                         virtualDevice /* virtualDeviceToken */,
                         mock(DisplayWindowPolicyController.class),
                         PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
         int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId;
 
         // Create a second virtual display. With the flag VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP,
@@ -838,6 +870,7 @@
                         virtualDevice /* virtualDeviceToken */,
                         mock(DisplayWindowPolicyController.class),
                         PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
         int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId;
 
         assertNotEquals(
@@ -881,6 +914,7 @@
                         virtualDevice /* virtualDeviceToken */,
                         mock(DisplayWindowPolicyController.class),
                         PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         // Check that FLAG_ALWAYS_UNLOCKED is set.
         assertNotEquals(
@@ -906,6 +940,7 @@
                         virtualDevice /* virtualDeviceToken */,
                         mock(DisplayWindowPolicyController.class),
                         PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         // Check that FLAG_ALWAYS_UNLOCKED is set.
         assertNotEquals(
@@ -929,6 +964,7 @@
                         null /* virtualDeviceToken */,
                         mock(DisplayWindowPolicyController.class),
                         PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         // Check that FLAG_ALWAYS_UNLOCKED is not set.
         assertEquals(
@@ -960,6 +996,7 @@
                 .setFlags(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
         final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
                 mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         // The second virtual display requests to mirror the first virtual display.
         final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
@@ -971,6 +1008,7 @@
         final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
                 mMockAppToken2 /* callback */, null /* projection */,
                 PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
 
         // flush the handler
@@ -985,6 +1023,54 @@
                 Display.INVALID_DISPLAY);
     }
 
+    @Test
+    public void testCreateVirtualDisplay_setContentRecordingSessionSuccess() throws Exception {
+        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+        when(mMockWindowManagerInternal
+                .setContentRecordingSession(any(ContentRecordingSession.class)))
+                .thenReturn(true);
+
+        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+                VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+        builder.setUniqueId("uniqueId --- setContentRecordingSession true");
+        builder.setContentRecordingSession(
+                ContentRecordingSession.createDisplaySession(new Binder("")));
+
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        registerDefaultDisplays(displayManager);
+        displayManager.windowManagerAndInputReady();
+
+        DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+        final int displayId = binderService.createVirtualDisplay(builder.build(),
+                mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+        assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+    }
+
+    @Test
+    public void testCreateVirtualDisplay_setContentRecordingSessionFail() throws Exception {
+        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+        when(mMockWindowManagerInternal
+                .setContentRecordingSession(any(ContentRecordingSession.class)))
+                .thenReturn(false);
+
+        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+                VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+        builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+        builder.setContentRecordingSession(
+                ContentRecordingSession.createDisplaySession(new Binder("")));
+
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        registerDefaultDisplays(displayManager);
+        displayManager.windowManagerAndInputReady();
+
+        DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+        final int displayId = binderService.createVirtualDisplay(builder.build(),
+                mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+        assertThat(displayId).isEqualTo(Display.INVALID_DISPLAY);
+    }
+
     /**
      * Tests that the virtual display is created with
      * {@link VirtualDisplayConfig.Builder#setSurface(Surface)}
@@ -1011,6 +1097,7 @@
         builder.setUniqueId(uniqueId);
         final int displayId = binderService.createVirtualDisplay(builder.build(),
                 mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
 
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
 
@@ -1043,6 +1130,7 @@
 
         int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
                 null /* projection */, PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
         displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
         DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
@@ -1093,7 +1181,6 @@
 
         registerDefaultDisplays(displayManager);
 
-        DisplayManagerService.BinderService bs = displayManager.new BinderService();
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
 
         when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn(
@@ -1111,6 +1198,7 @@
         int displayId = localService.createVirtualDisplay(builder.build(),
                 mMockAppToken /* callback */, virtualDevice /* virtualDeviceToken */,
                 mock(DisplayWindowPolicyController.class), PACKAGE_NAME);
+        verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
         displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
         DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
index 178670e..397d7b5 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -21,7 +21,6 @@
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
 
 import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
-import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_RETENTION_MS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -66,6 +65,7 @@
     public void removesOldWakeups() {
         // The xml resource doesn't matter for this test.
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1, mHandler);
+        final long retention = obj.mConfig.WAKEUP_STATS_RETENTION_MS;
 
         final Set<Long> timestamps = new HashSet<>();
         final long firstWakeup = 453192;
@@ -73,22 +73,21 @@
         obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_UNKNOWN_IRQ);
         timestamps.add(firstWakeup);
         for (int i = 1; i < 1000; i++) {
-            final long delta = mRandom.nextLong(WAKEUP_RETENTION_MS);
+            final long delta = mRandom.nextLong(retention);
             if (timestamps.add(firstWakeup + delta)) {
                 obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_UNKNOWN_IRQ);
             }
         }
         assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
 
-        obj.noteWakeupTimeAndReason(firstWakeup + WAKEUP_RETENTION_MS + 1242, 231,
+        obj.noteWakeupTimeAndReason(firstWakeup + retention + 1242, 231,
                 KERNEL_REASON_UNKNOWN_IRQ);
         assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
 
         for (int i = 0; i < 100; i++) {
-            final long now = mRandom.nextLong(WAKEUP_RETENTION_MS + 1, 100 * WAKEUP_RETENTION_MS);
+            final long now = mRandom.nextLong(retention + 1, 100 * retention);
             obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_UNKNOWN_IRQ);
-            assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - WAKEUP_RETENTION_MS))
-                    .isLessThan(0);
+            assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - retention)).isLessThan(0);
         }
     }
 
diff --git a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java
index 2c5fcb8..68067d2 100644
--- a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java
+++ b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java
@@ -191,9 +191,9 @@
     public void reset(long transactionId) {
         try {
             synchronized(mGadgetProxyLock) {
-                if (android.hardware.usb.gadget.V1_2.IUsbGadget.castFrom(mGadgetProxy) != null) {
-                    android.hardware.usb.gadget.V1_2.IUsbGadget gadgetProxy =
-                    android.hardware.usb.gadget.V1_2.IUsbGadget.castFrom(mGadgetProxy);
+                if (android.hardware.usb.gadget.V1_1.IUsbGadget.castFrom(mGadgetProxy) != null) {
+                    android.hardware.usb.gadget.V1_1.IUsbGadget gadgetProxy =
+                    android.hardware.usb.gadget.V1_1.IUsbGadget.castFrom(mGadgetProxy);
                     gadgetProxy.reset();
                 }
             }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 918ae79..bf12b9c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -7608,6 +7608,55 @@
         public static final String KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL =
                 KEY_PREFIX + "emergency_requires_volte_enabled_bool";
 
+        /**
+         * This values indicates that the cross SIM redialing timer shall be disabled.
+         *
+         * @see #KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT
+         * @see #KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT
+         * @hide
+         */
+        public static final int REDIAL_TIMER_DISABLED = 0;
+
+        /**
+         * A timer to guard the max attempting time on current SIM slot so that modem will not
+         * stuck in current SIM slot for long time. On timer expiry, if emergency call on the
+         * other SIM slot is preferable, telephony shall cancel the emergency call and place the
+         * call on the other SIM slot. If this value is set to {@link #REDIAL_TIMER_DISABLED}, then
+         * the timer will never be started and domain selection continues on the current SIM slot.
+         * This value should be greater than the value of {@link #KEY_EMERGENCY_SCAN_TIMER_SEC_INT}.
+         *
+         * The default value for the timer is 120 seconds.
+         * @hide
+         */
+        public static final String KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT =
+                KEY_PREFIX + "cross_stack_redial_timer_sec_int";
+
+        /**
+         * If emergency calls are only allowed with normal-registered service and UE should get
+         * normal service in a short time with acquired band information, telephony
+         * expects dialing emergency call will be completed in a short time.
+         * If dialing is not completed with in a certain timeout, telephony shall place on
+         * another SIM slot. If this value is set to {@link #REDIAL_TIMER_DISABLED}, then the timer
+         * will never be started and domain selection continues on the current SIM slot.
+         * The timer shall be started for the first trial of each subscription and shall be ignored
+         * in the roaming networks and non-domestic networks.
+         *
+         * The default value for the timer is {@link #REDIAL_TIMER_DISABLED}.
+         * @hide
+         */
+        public static final String KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT =
+                KEY_PREFIX + "quick_cross_stack_redial_timer_sec_int";
+
+        /**
+         * Indicates whether the quick cross stack redial timer will be triggered only when
+         * the device is registered to the network.
+         *
+         * The default value is {@code true}.
+         * @hide
+         */
+        public static final String KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL =
+                KEY_PREFIX + "start_quick_cross_stack_redial_timer_when_registered_bool";
+
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putBoolean(KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL, false);
@@ -7674,6 +7723,10 @@
             defaults.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, false);
             defaults.putStringArray(KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY,
                     new String[0]);
+            defaults.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, 120);
+            defaults.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);
+            defaults.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL,
+                    true);
 
             return defaults;
         }
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 3b4cf75..1cfd22c 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -235,6 +235,23 @@
     /** Call failed/dropped because of a network detach. */
     public static final int NETWORK_DETACH                                   = 261;
 
+    /**
+     * Dialing emergency calls is currently unavailable.
+     * The call should be redialed on the other subscription silently.
+     * If there is no other subscription available, the call may be redialed
+     * on this subscription again.
+     * @hide
+     */
+    public static final int EMERGENCY_TEMP_FAILURE                           = 325;
+    /**
+     * Dialing emergency calls is currently unavailable.
+     * The call should be redialed on the other subscription silently.
+     * Even if there is no other subscription available, the call should not
+     * be redialed on this subscription again.
+     * @hide
+     */
+    public static final int EMERGENCY_PERM_FAILURE                           = 326;
+
     /** Mobile station (MS) is locked until next power cycle. */
     public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE                    = 1000;
     /** Drop call. */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 19ecf6a..05abf9f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -53,7 +53,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
     /** {@inheritDoc} */
     override val transition: FlickerBuilder.() -> Unit
         get() = {
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
new file mode 100644
index 0000000..46899f3
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.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
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+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)
+class OpenAppColdFromIconCfArm(flicker: FlickerTest) : OpenAppColdFromIcon(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> {
+            // TAPL fails on landscape mode b/240916028
+            return FlickerTestFactory.nonRotationTests(
+                    supportedNavigationModes = listOf(NavBar.MODE_3BUTTON)
+            )
+        }
+    }
+}
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 da98523..b848e63 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
@@ -93,8 +93,7 @@
                 .then()
                 // Animation starts, but the app may not be drawn yet which means the Splash
                 // may be visible.
-                .isInvisible(testApp, isOptional = true)
-                .isVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+                .isSplashScreenVisibleFor(testApp, isOptional = true)
                 .then()
                 // App shows up with the custom animation starting at alpha=1.
                 .isVisible(testApp)