Merge "Move res/drawable-television out of SystemUI" into main
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
index bc8fc53..2de6f36 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -147,28 +147,4 @@
             out.clear();
         }
     }
-
-    @ManualBenchmarkState.ManualBenchmarkTest(
-            warmupDurationNs = WARMUP_DURATION_NS,
-            targetTestDurationNs = TARGET_TEST_DURATION_NS)
-    @Test
-    public void testSetSystemFontMap() throws Exception {
-        SharedMemory memory = null;
-        ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState();
-
-        long elapsedTime = 0;
-        while (state.keepRunning(elapsedTime)) {
-            // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit
-            // (max_map_count).
-            Typeface.destroySystemFontMap();
-            Typeface.loadPreinstalledSystemFontMap();
-            if (memory != null) {
-                memory.close();
-            }
-            memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
-            long startTime = System.nanoTime();
-            Typeface.setSystemFontMap(memory);
-            elapsedTime = System.nanoTime() - startTime;
-        }
-    }
 }
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 9a0053f..22cc059 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -483,6 +483,22 @@
 }
 
 java_library {
+    name: "android_test_frameworks_core_stubs_current.from-source",
+    static_libs: [
+        "all-updatable-modules-system-stubs",
+        "android-non-updatable.stubs.test",
+        "private-stub-annotations-jar",
+    ],
+    defaults: [
+        "android.jar_defaults",
+        "android_stubs_dists_default",
+    ],
+    dist: {
+        dir: "apistubs/android/test-core",
+    },
+}
+
+java_library {
     name: "android_module_lib_stubs_current.from-source",
     defaults: [
         "android.jar_defaults",
diff --git a/api/api.go b/api/api.go
index a003aba..d5c6145 100644
--- a/api/api.go
+++ b/api/api.go
@@ -146,7 +146,7 @@
 	metalavaCmd := "$(location metalava)"
 	// Silence reflection warnings. See b/168689341
 	metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
-	metalavaCmd += " --quiet --no-banner --format=v2 "
+	metalavaCmd += " --quiet merge-signatures --format=v2 "
 
 	filename := txt.TxtFilename
 	if txt.Scope != "public" {
@@ -156,7 +156,7 @@
 	props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
 	props.Tools = []string{"metalava"}
 	props.Out = []string{filename}
-	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
+	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
 	props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
 	props.Dists = []android.Dist{
 		{
@@ -370,6 +370,7 @@
 		"android_stubs_current",
 		"android_system_stubs_current",
 		"android_test_stubs_current",
+		"android_test_frameworks_core_stubs_current",
 		"android_module_lib_stubs_current",
 		"android_system_server_stubs_current",
 	}
diff --git a/core/api/current.txt b/core/api/current.txt
index 363e9d4..9ba84c9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -47271,9 +47271,9 @@
   }
 
   public class BoringLayout extends android.text.Layout implements android.text.TextUtils.EllipsizeCallback {
-    ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
-    ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
-    ctor public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
+    ctor @Deprecated public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
+    ctor @Deprecated public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
+    ctor @Deprecated public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
     method public void ellipsized(int, int);
     method public int getBottomPadding();
     method public int getEllipsisCount(int);
@@ -47286,12 +47286,12 @@
     method public int getLineTop(int);
     method public int getParagraphDirection(int);
     method public int getTopPadding();
-    method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint);
-    method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics);
-    method @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics);
-    method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
-    method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
-    method @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
+    method @Deprecated public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint);
+    method @Deprecated public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics);
+    method @Deprecated @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics);
+    method @Deprecated public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
+    method @Deprecated public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
+    method @Deprecated @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
     method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
     method @NonNull public android.text.BoringLayout replaceOrMake(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
     method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
@@ -47316,7 +47316,6 @@
     method public int getBottomPadding();
     method public int getEllipsisCount(int);
     method public int getEllipsisStart(int);
-    method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
     method public boolean getLineContainsTab(int);
     method public int getLineCount();
     method public int getLineDescent(int);
@@ -47489,20 +47488,26 @@
     method public void drawBackground(@NonNull android.graphics.Canvas);
     method public void drawText(@NonNull android.graphics.Canvas);
     method public void fillCharacterBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int);
-    method public final android.text.Layout.Alignment getAlignment();
+    method @NonNull public final android.text.Layout.Alignment getAlignment();
     method public abstract int getBottomPadding();
+    method public final int getBreakStrategy();
     method public void getCursorPath(int, android.graphics.Path, CharSequence);
     method public static float getDesiredWidth(CharSequence, android.text.TextPaint);
     method public static float getDesiredWidth(CharSequence, int, int, android.text.TextPaint);
     method public abstract int getEllipsisCount(int);
     method public abstract int getEllipsisStart(int);
-    method public int getEllipsizedWidth();
+    method @Nullable public final android.text.TextUtils.TruncateAt getEllipsize();
+    method @IntRange(from=0) public int getEllipsizedWidth();
     method public int getHeight();
+    method public final int getHyphenationFrequency();
+    method public final int getJustificationMode();
+    method @Nullable public final int[] getLeftIndents();
     method public final int getLineAscent(int);
     method public final int getLineBaseline(int);
     method public final int getLineBottom(int);
     method public int getLineBottom(int, boolean);
     method public int getLineBounds(int, android.graphics.Rect);
+    method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
     method public abstract boolean getLineContainsTab(int);
     method public abstract int getLineCount();
     method public abstract int getLineDescent(int);
@@ -47513,29 +47518,35 @@
     method public float getLineLeft(int);
     method public float getLineMax(int);
     method public float getLineRight(int);
+    method public final float getLineSpacingAmount();
+    method public final float getLineSpacingMultiplier();
     method public abstract int getLineStart(int);
     method public abstract int getLineTop(int);
     method public int getLineVisibleEnd(int);
     method public float getLineWidth(int);
+    method @IntRange(from=1) public final int getMaxLines();
     method public int getOffsetForHorizontal(int, float);
     method public int getOffsetToLeftOf(int);
     method public int getOffsetToRightOf(int);
-    method public final android.text.TextPaint getPaint();
+    method @NonNull public final android.text.TextPaint getPaint();
     method public final android.text.Layout.Alignment getParagraphAlignment(int);
     method public abstract int getParagraphDirection(int);
     method public final int getParagraphLeft(int);
     method public final int getParagraphRight(int);
     method public float getPrimaryHorizontal(int);
     method @Nullable public int[] getRangeForRect(@NonNull android.graphics.RectF, @NonNull android.text.SegmentFinder, @NonNull android.text.Layout.TextInclusionStrategy);
+    method @Nullable public final int[] getRightIndents();
     method public float getSecondaryHorizontal(int);
     method public void getSelectionPath(int, int, android.graphics.Path);
     method public final float getSpacingAdd();
     method public final float getSpacingMultiplier();
-    method public final CharSequence getText();
+    method @NonNull public final CharSequence getText();
+    method @NonNull public final android.text.TextDirectionHeuristic getTextDirectionHeuristic();
     method public abstract int getTopPadding();
-    method public final int getWidth();
+    method @IntRange(from=0) public final int getWidth();
     method public final void increaseWidthTo(int);
     method public boolean isFallbackLineSpacingEnabled();
+    method public final boolean isIncludeFontPadding();
     method public boolean isRtlCharAt(int);
     method protected final boolean isSpanned();
     field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -47563,6 +47574,26 @@
     enum_constant public static final android.text.Layout.Alignment ALIGN_OPPOSITE;
   }
 
+  public static final class Layout.Builder {
+    ctor public Layout.Builder(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextPaint, @IntRange(from=0) int);
+    method @NonNull public android.text.Layout build();
+    method @NonNull public android.text.Layout.Builder setAlignment(@NonNull android.text.Layout.Alignment);
+    method @NonNull public android.text.Layout.Builder setBreakStrategy(int);
+    method @NonNull public android.text.Layout.Builder setEllipsize(@Nullable android.text.TextUtils.TruncateAt);
+    method @NonNull public android.text.Layout.Builder setEllipsizedWidth(@IntRange(from=0) int);
+    method @NonNull public android.text.Layout.Builder setFallbackLineSpacingEnabled(boolean);
+    method @NonNull public android.text.Layout.Builder setHyphenationFrequency(int);
+    method @NonNull public android.text.Layout.Builder setIncludeFontPadding(boolean);
+    method @NonNull public android.text.Layout.Builder setJustificationMode(int);
+    method @NonNull public android.text.Layout.Builder setLeftIndents(@Nullable int[]);
+    method @NonNull public android.text.Layout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
+    method @NonNull public android.text.Layout.Builder setLineSpacingAmount(float);
+    method @NonNull public android.text.Layout.Builder setLineSpacingMultiplier(float);
+    method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int);
+    method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]);
+    method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic);
+  }
+
   public static class Layout.Directions {
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d480315..6141583 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10389,6 +10389,7 @@
     field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1
     field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
     field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
+    field public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
     field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
     field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
     field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3429c7c..e61c39f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2358,6 +2358,7 @@
   public final class PowerManager {
     method public boolean areAutoPowerSaveModesEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void forceLowPowerStandbyActive(boolean);
+    method public boolean isBatterySaverSupported();
     field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
     field @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public static final int SYSTEM_WAKELOCK = -2147483648; // 0x80000000
   }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9121cf0..7be4b15 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -179,6 +179,10 @@
     @GuardedBy("mDelegates")
     private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
 
+    @NonNull
+    @GuardedBy("mPackageMonitorCallbacks")
+    private final ArraySet<IRemoteCallback> mPackageMonitorCallbacks = new ArraySet<>();
+
     UserManager getUserManager() {
         if (mUserManager == null) {
             mUserManager = UserManager.get(mContext);
@@ -3926,6 +3930,14 @@
         Objects.requireNonNull(callback);
         try {
             mPM.registerPackageMonitorCallback(callback, userId);
+            synchronized (mPackageMonitorCallbacks) {
+                if (mPackageMonitorCallbacks.contains(callback)) {
+                    throw new IllegalStateException(
+                            "registerPackageMonitorCallback: callback already registered: "
+                                    + callback);
+                }
+                mPackageMonitorCallbacks.add(callback);
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3936,6 +3948,9 @@
         Objects.requireNonNull(callback);
         try {
             mPM.unregisterPackageMonitorCallback(callback);
+            synchronized (mPackageMonitorCallbacks) {
+                mPackageMonitorCallbacks.remove(callback);
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
index 763246e..dc66542 100644
--- a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
+++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.biometrics.AuthenticateOptions;
+import android.hardware.biometrics.common.AuthenticateReason;
 import android.os.Parcelable;
 
 import com.android.internal.util.DataClass;
@@ -85,7 +86,16 @@
         return null;
     }
 
-
+    /**
+     * The Vendor extension, if any.
+     *
+     * This option may be present when a vendor would like to send additional information for each
+     * auth attempt.
+     */
+    @Nullable private AuthenticateReason.Vendor mVendorReason;
+    private static AuthenticateReason.Vendor defaultVendorReason() {
+        return null;
+    }
 
     // Code below generated by codegen v1.0.23.
     //
@@ -107,7 +117,8 @@
             boolean ignoreEnrollmentState,
             @AuthenticateOptions.DisplayState int displayState,
             @NonNull String opPackageName,
-            @Nullable String attributionTag) {
+            @Nullable String attributionTag,
+            @Nullable AuthenticateReason.Vendor vendorReason) {
         this.mUserId = userId;
         this.mSensorId = sensorId;
         this.mIgnoreEnrollmentState = ignoreEnrollmentState;
@@ -118,6 +129,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mOpPackageName);
         this.mAttributionTag = attributionTag;
+        this.mVendorReason = vendorReason;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -176,6 +188,17 @@
     }
 
     /**
+     * The Vendor extension, if any.
+     *
+     * This option may be present when a vendor would like to send additional information for each
+     * auth attempt.
+     */
+    @DataClass.Generated.Member
+    public @Nullable AuthenticateReason.Vendor getVendorReason() {
+        return mVendorReason;
+    }
+
+    /**
      * The sensor id for this operation.
      */
     @DataClass.Generated.Member
@@ -209,6 +232,18 @@
         return this;
     }
 
+    /**
+     * The Vendor extension, if any.
+     *
+     * This option may be present when a vendor would like to send additional information for each
+     * auth attempt.
+     */
+    @DataClass.Generated.Member
+    public @NonNull FingerprintAuthenticateOptions setVendorReason(@NonNull AuthenticateReason.Vendor value) {
+        mVendorReason = value;
+        return this;
+    }
+
     @Override
     @DataClass.Generated.Member
     public boolean equals(@Nullable Object o) {
@@ -227,7 +262,8 @@
                 && mIgnoreEnrollmentState == that.mIgnoreEnrollmentState
                 && mDisplayState == that.mDisplayState
                 && java.util.Objects.equals(mOpPackageName, that.mOpPackageName)
-                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag);
+                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+                && java.util.Objects.equals(mVendorReason, that.mVendorReason);
     }
 
     @Override
@@ -243,6 +279,7 @@
         _hash = 31 * _hash + mDisplayState;
         _hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName);
         _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mVendorReason);
         return _hash;
     }
 
@@ -255,12 +292,14 @@
         byte flg = 0;
         if (mIgnoreEnrollmentState) flg |= 0x4;
         if (mAttributionTag != null) flg |= 0x20;
+        if (mVendorReason != null) flg |= 0x40;
         dest.writeByte(flg);
         dest.writeInt(mUserId);
         dest.writeInt(mSensorId);
         dest.writeInt(mDisplayState);
         dest.writeString(mOpPackageName);
         if (mAttributionTag != null) dest.writeString(mAttributionTag);
+        if (mVendorReason != null) dest.writeTypedObject(mVendorReason, flags);
     }
 
     @Override
@@ -281,6 +320,7 @@
         int displayState = in.readInt();
         String opPackageName = in.readString();
         String attributionTag = (flg & 0x20) == 0 ? null : in.readString();
+        AuthenticateReason.Vendor vendorReason = (flg & 0x40) == 0 ? null : (AuthenticateReason.Vendor) in.readTypedObject(AuthenticateReason.Vendor.CREATOR);
 
         this.mUserId = userId;
         this.mSensorId = sensorId;
@@ -292,6 +332,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mOpPackageName);
         this.mAttributionTag = attributionTag;
+        this.mVendorReason = vendorReason;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -323,6 +364,7 @@
         private @AuthenticateOptions.DisplayState int mDisplayState;
         private @NonNull String mOpPackageName;
         private @Nullable String mAttributionTag;
+        private @Nullable AuthenticateReason.Vendor mVendorReason;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -400,10 +442,24 @@
             return this;
         }
 
+        /**
+         * The Vendor extension, if any.
+         *
+         * This option may be present when a vendor would like to send additional information for each
+         * auth attempt.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setVendorReason(@NonNull AuthenticateReason.Vendor value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40;
+            mVendorReason = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull FingerprintAuthenticateOptions build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x40; // Mark builder used
+            mBuilderFieldsSet |= 0x80; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mUserId = defaultUserId();
@@ -423,18 +479,22 @@
             if ((mBuilderFieldsSet & 0x20) == 0) {
                 mAttributionTag = defaultAttributionTag();
             }
+            if ((mBuilderFieldsSet & 0x40) == 0) {
+                mVendorReason = defaultVendorReason();
+            }
             FingerprintAuthenticateOptions o = new FingerprintAuthenticateOptions(
                     mUserId,
                     mSensorId,
                     mIgnoreEnrollmentState,
                     mDisplayState,
                     mOpPackageName,
-                    mAttributionTag);
+                    mAttributionTag,
+                    mVendorReason);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x40) != 0) {
+            if ((mBuilderFieldsSet & 0x80) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -442,10 +502,10 @@
     }
 
     @DataClass.Generated(
-            time = 1677119626721L,
+            time = 1689703591032L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java",
-            inputSignatures = "private final  int mUserId\nprivate  int mSensorId\nprivate final  boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  boolean defaultIgnoreEnrollmentState()\nprivate static  int defaultDisplayState()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+            inputSignatures = "private final  int mUserId\nprivate  int mSensorId\nprivate final  boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  boolean defaultIgnoreEnrollmentState()\nprivate static  int defaultDisplayState()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nprivate static  android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 43219bc..42c5626 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1656,6 +1656,8 @@
     public abstract CpuScalingPolicies getCpuScalingPolicies();
 
     public final static class HistoryTag {
+        public static final int HISTORY_TAG_POOL_OVERFLOW = -1;
+
         public String string;
         public int uid;
 
@@ -6826,10 +6828,11 @@
                     if (bd.mask == HistoryItem.STATE_WAKE_LOCK_FLAG && wakelockTag != null) {
                         didWake = true;
                         sb.append("=");
-                        if (longNames) {
+                        if (longNames
+                                || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
                             UserHandle.formatUid(sb, wakelockTag.uid);
                             sb.append(":\"");
-                            sb.append(wakelockTag.string);
+                            sb.append(wakelockTag.string.replace("\"", "\"\""));
                             sb.append("\"");
                         } else {
                             sb.append(wakelockTag.poolIdx);
@@ -6849,7 +6852,7 @@
         }
         if (!didWake && wakelockTag != null) {
             sb.append(longNames ? " wake_lock=" : ",w=");
-            if (longNames) {
+            if (longNames || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
                 UserHandle.formatUid(sb, wakelockTag.uid);
                 sb.append(":\"");
                 sb.append(wakelockTag.string);
@@ -7110,7 +7113,14 @@
                 if (rec.wakeReasonTag != null) {
                     if (checkin) {
                         item.append(",wr=");
-                        item.append(rec.wakeReasonTag.poolIdx);
+                        if (rec.wakeReasonTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+                            item.append(sUidToString.applyAsString(rec.wakeReasonTag.uid));
+                            item.append(":\"");
+                            item.append(rec.wakeReasonTag.string.replace("\"", "\"\""));
+                            item.append("\"");
+                        } else {
+                            item.append(rec.wakeReasonTag.poolIdx);
+                        }
                     } else {
                         item.append(" wake_reason=");
                         item.append(rec.wakeReasonTag.uid);
@@ -7138,7 +7148,15 @@
                     }
                     item.append("=");
                     if (checkin) {
-                        item.append(rec.eventTag.poolIdx);
+                        if (rec.eventTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+                            item.append(HISTORY_EVENT_INT_FORMATTERS[idx]
+                                    .applyAsString(rec.eventTag.uid));
+                            item.append(":\"");
+                            item.append(rec.eventTag.string.replace("\"", "\"\""));
+                            item.append("\"");
+                        } else {
+                            item.append(rec.eventTag.poolIdx);
+                        }
                     } else {
                         item.append(HISTORY_EVENT_INT_FORMATTERS[idx]
                                 .applyAsString(rec.eventTag.uid));
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index d9d14b0..0456a33 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -81,7 +81,8 @@
             BUGREPORT_MODE_REMOTE,
             BUGREPORT_MODE_WEAR,
             BUGREPORT_MODE_TELEPHONY,
-            BUGREPORT_MODE_WIFI
+            BUGREPORT_MODE_WIFI,
+            BUGREPORT_MODE_ONBOARDING
     })
     public @interface BugreportMode {}
 
@@ -121,6 +122,11 @@
     public static final int BUGREPORT_MODE_WIFI = IDumpstate.BUGREPORT_MODE_WIFI;
 
     /**
+     * Options for a lightweight bugreport intended to be taken for onboarding-related flows.
+     */
+    public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
+
+    /**
      * Defines acceptable flags for customizing bugreport requests.
      * @hide
      */
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index be7c1e5..dbb6f92 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -60,6 +60,7 @@
     boolean isPowerSaveMode();
     PowerSaveState getPowerSaveState(int serviceType);
     boolean setPowerSaveModeEnabled(boolean mode);
+    boolean isBatterySaverSupported();
     BatterySaverPolicyConfig getFullPowerSavePolicy();
     boolean setFullPowerSavePolicy(in BatterySaverPolicyConfig config);
     boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d676509..4174c1c 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1936,6 +1936,20 @@
     }
 
     /**
+     * Returns true if Battery Saver is supported on this device.
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean isBatterySaverSupported() {
+        try {
+            return mService.isBatterySaverSupported();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets the current policy for full power save mode.
      *
      * @return The {@link BatterySaverPolicyConfig} which is currently set for the full power save
diff --git a/core/java/android/os/PowerMonitor.java b/core/java/android/os/PowerMonitor.java
index ebdd463..5fb0df7 100644
--- a/core/java/android/os/PowerMonitor.java
+++ b/core/java/android/os/PowerMonitor.java
@@ -23,6 +23,10 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * A PowerMonitor represents either a Channel aka ODPM rail (on-device power monitor) or an
+ * EnergyConsumer, as defined in
+ * <a href="https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/main/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats">android.hardware.power.stats</a>
+ *
  * @hide
  */
 public final class PowerMonitor implements Parcelable {
@@ -92,6 +96,7 @@
         return 0;
     }
 
+    @NonNull
     public static final Creator<PowerMonitor> CREATOR = new Creator<>() {
         @Override
         public PowerMonitor createFromParcel(@NonNull Parcel in) {
diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java
index 3d7f859..e767059 100644
--- a/core/java/android/os/PowerMonitorReadings.java
+++ b/core/java/android/os/PowerMonitorReadings.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 
 import java.util.Arrays;
@@ -43,8 +44,8 @@
      * @param powerMonitors array of power monitor (ODPM) rails, sorted by PowerMonitor.index
      * @hide
      */
-    public PowerMonitorReadings(PowerMonitor[] powerMonitors,
-            long[] energyUws, long[] timestampsMs) {
+    public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
+            @NonNull long[] energyUws, @NonNull long[] timestampsMs) {
         mPowerMonitors = powerMonitors;
         mEnergyUws = energyUws;
         mTimestampsMs = timestampsMs;
@@ -55,7 +56,7 @@
      * Does not persist across reboots.
      * Represents total energy: both on-battery and plugged-in.
      */
-    public long getConsumedEnergyUws(PowerMonitor powerMonitor) {
+    public long getConsumedEnergyUws(@NonNull PowerMonitor powerMonitor) {
         int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
         if (offset >= 0) {
             return mEnergyUws[offset];
@@ -64,9 +65,10 @@
     }
 
     /**
-     * Elapsed realtime when the snapshot was taken.
+     * Elapsed realtime, in milliseconds, when the snapshot was taken.
      */
-    public long getTimestampMs(PowerMonitor powerMonitor) {
+    @ElapsedRealtimeLong
+    public long getTimestamp(@NonNull PowerMonitor powerMonitor) {
         int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
         if (offset >= 0) {
             return mTimestampsMs[offset];
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ba1f979..d8cf4de 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4618,6 +4618,26 @@
     }
 
     /**
+     * Returns number of full users on the device.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public int getFullUserCount() {
+        List<UserInfo> users = getUsers(/* excludePartial= */ true, /* excludeDying= */ true,
+                /* excludePreCreated= */ true);
+        int count = 0;
+        for (UserInfo user : users) {
+            if (user.isFull()) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
      * @deprecated use {@link #getAliveUsers()} for {@code getUsers(true)}, or
      * {@link #getUsers()} for @code getUsers(false)}.
      *
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index ab30a8b..dfc43f4 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -24,6 +24,8 @@
 import android.os.BatteryStats;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
 import android.os.IPowerStatsService;
 import android.os.PowerMonitor;
 import android.os.PowerMonitorReadings;
@@ -36,8 +38,8 @@
 
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
+import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * Provides access to data about how various system resources are used by applications.
@@ -62,7 +64,8 @@
     private final IBatteryStats mBatteryStats;
     @Nullable
     private final IPowerStatsService mPowerStats;
-    private PowerMonitor[] mPowerMonitorsInfo;
+    private List<PowerMonitor> mPowerMonitorsInfo;
+    private final Object mPowerMonitorsLock = new Object();
 
     /**
      * Construct a new SystemHealthManager object.
@@ -161,53 +164,68 @@
      * @hide
      */
     @NonNull
-    public PowerMonitor[] getSupportedPowerMonitors() {
-        synchronized (this) {
+    public List<PowerMonitor> getSupportedPowerMonitors() {
+        synchronized (mPowerMonitorsLock) {
             if (mPowerMonitorsInfo != null) {
                 return mPowerMonitorsInfo;
             }
+        }
+        ConditionVariable lock = new ConditionVariable();
+        // Populate mPowerMonitorsInfo by side-effect
+        getSupportedPowerMonitors(null, unused -> lock.open());
+        lock.block();
 
-            CompletableFuture<PowerMonitor[]> future = new CompletableFuture<>();
-            getSupportedPowerMonitors(future);
-            try {
-                return future.get();
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
+        synchronized (mPowerMonitorsLock) {
+            return mPowerMonitorsInfo;
         }
     }
 
     /**
-     * Retrieves a list of supported power monitors, see {@link #getSupportedPowerMonitors()}
+     * Asynchronously retrieves a list of supported power monitors, see
+     * {@link #getSupportedPowerMonitors()}
+     *
+     * @param handler optional Handler to deliver the callback. If not supplied, the callback
+     *                may be invoked on an arbitrary thread.
+     * @param onResult callback for the result
      *
      * @hide
      */
-    public void getSupportedPowerMonitors(@NonNull CompletableFuture<PowerMonitor[]> future) {
-        synchronized (this) {
+    public void getSupportedPowerMonitors(@Nullable Handler handler,
+            @NonNull Consumer<List<PowerMonitor>> onResult) {
+        final List<PowerMonitor> result;
+        synchronized (mPowerMonitorsLock) {
             if (mPowerMonitorsInfo != null) {
-                future.complete(mPowerMonitorsInfo);
-                return;
+                result = mPowerMonitorsInfo;
+            } else if (mPowerStats == null) {
+                mPowerMonitorsInfo = List.of();
+                result = mPowerMonitorsInfo;
+            } else {
+                result = null;
             }
-            try {
-                if (mPowerStats == null) {
-                    mPowerMonitorsInfo = new PowerMonitor[0];
-                    future.complete(mPowerMonitorsInfo);
-                    return;
-                }
-
-                mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) {
-                    @Override
-                    protected void onReceiveResult(int resultCode, Bundle resultData) {
-                        synchronized (this) {
-                            mPowerMonitorsInfo = resultData.getParcelableArray(
-                                    IPowerStatsService.KEY_MONITORS, PowerMonitor.class);
-                        }
-                        future.complete(mPowerMonitorsInfo);
+        }
+        if (result != null) {
+            if (handler != null) {
+                handler.post(() -> onResult.accept(result));
+            } else {
+                onResult.accept(result);
+            }
+            return;
+        }
+        try {
+            mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) {
+                @Override
+                protected void onReceiveResult(int resultCode, Bundle resultData) {
+                    PowerMonitor[] array = resultData.getParcelableArray(
+                            IPowerStatsService.KEY_MONITORS, PowerMonitor.class);
+                    List<PowerMonitor> result = array != null ? Arrays.asList(array) : List.of();
+                    synchronized (mPowerMonitorsLock) {
+                        mPowerMonitorsInfo = result;
                     }
-                });
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+                    onResult.accept(result);
+                }
+            });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -215,54 +233,74 @@
      * Retrieves the accumulated power consumption reported by the specified power monitors.
      *
      * @param powerMonitors power monitors to be returned.
+     *
      * @hide
      */
     @NonNull
-    public PowerMonitorReadings getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors) {
-        CompletableFuture<PowerMonitorReadings> future = new CompletableFuture<>();
-        getPowerMonitorReadings(powerMonitors, future);
-        try {
-            return future.get();
-        } catch (InterruptedException | ExecutionException e) {
-            throw new RuntimeException(e);
+    public PowerMonitorReadings getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors) {
+        PowerMonitorReadings[] outReadings = new PowerMonitorReadings[1];
+        RuntimeException[] outException = new RuntimeException[1];
+        ConditionVariable lock = new ConditionVariable();
+        getPowerMonitorReadings(powerMonitors, null,
+                pms -> {
+                    outReadings[0] = pms;
+                    lock.open();
+                },
+                error -> {
+                    outException[0] = error;
+                    lock.open();
+                }
+        );
+        lock.block();
+        if (outException[0] != null) {
+            throw outException[0];
         }
+        return outReadings[0];
     }
 
     private static final Comparator<PowerMonitor> POWER_MONITOR_COMPARATOR =
             Comparator.comparingInt(pm -> pm.index);
 
     /**
+     * Asynchronously retrieves the accumulated power consumption reported by the specified power
+     * monitors.
+     *
      * @param powerMonitors power monitors to be retrieved.
+     * @param handler       optional Handler to deliver the callbacks. If not supplied, the callback
+     *                      may be invoked on an arbitrary thread.
+     * @param onSuccess     callback for the result
+     * @param onError       callback invoked in case of an error
+     *
      * @hide
      */
-    public void getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
-            @NonNull CompletableFuture<PowerMonitorReadings> future) {
+    public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors,
+            @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess,
+            @NonNull Consumer<RuntimeException> onError) {
         if (mPowerStats == null) {
-            future.completeExceptionally(
-                    new IllegalArgumentException("Unsupported power monitor"));
+            onError.accept(new IllegalArgumentException("Unsupported power monitor"));
             return;
         }
 
-        Arrays.sort(powerMonitors, POWER_MONITOR_COMPARATOR);
-        int[] indices = new int[powerMonitors.length];
-        for (int i = 0; i < powerMonitors.length; i++) {
-            indices[i] = powerMonitors[i].index;
+        PowerMonitor[] powerMonitorsArray =
+                powerMonitors.toArray(new PowerMonitor[powerMonitors.size()]);
+        Arrays.sort(powerMonitorsArray, POWER_MONITOR_COMPARATOR);
+        int[] indices = new int[powerMonitors.size()];
+        for (int i = 0; i < powerMonitors.size(); i++) {
+            indices[i] = powerMonitorsArray[i].index;
         }
         try {
-            mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) {
+            mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) {
                 @Override
                 protected void onReceiveResult(int resultCode, Bundle resultData) {
                     if (resultCode == IPowerStatsService.RESULT_SUCCESS) {
-                        future.complete(new PowerMonitorReadings(powerMonitors,
+                        onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray,
                                 resultData.getLongArray(IPowerStatsService.KEY_ENERGY),
                                 resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS)));
                     } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) {
-                        future.completeExceptionally(
-                                new IllegalArgumentException("Unsupported power monitor"));
+                        onError.accept(new IllegalArgumentException("Unsupported power monitor"));
                     } else {
-                        future.completeExceptionally(
-                                new IllegalStateException(
-                                        "Unrecognized result code " + resultCode));
+                        onError.accept(new IllegalStateException(
+                                "Unrecognized result code " + resultCode));
                     }
                 }
             });
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2e62e03..3f2be51 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11265,6 +11265,19 @@
                 "navigation_mode";
 
         /**
+         * The value is from another(source) device's {@link #NAVIGATION_MODE} during restore.
+         * It's supposed to be written only by
+         * {@link com.android.providers.settings.SettingsHelper}.
+         * This setting should not be added into backup array.
+         * <p>Value: -1 = Can't get value from restore(default),
+         *  0 = 3 button,
+         *  1 = 2 button,
+         *  2 = fully gestural.
+         * @hide
+         */
+        public static final String NAVIGATION_MODE_RESTORE = "navigation_mode_restore";
+
+        /**
          * Scale factor for the back gesture inset size on the left side of the screen.
          * @hide
          */
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index b85cf6d..fce87db 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -285,6 +286,7 @@
         private IControlsSubscriber mCs;
         private boolean mEnforceStateless;
         private Context mContext;
+        private SubscriptionAdapter mSubscription;
 
         SubscriberProxy(boolean enforceStateless, IBinder token, IControlsSubscriber cs) {
             mEnforceStateless = enforceStateless;
@@ -300,11 +302,14 @@
 
         public void onSubscribe(Subscription subscription) {
             try {
-                mCs.onSubscribe(mToken, new SubscriptionAdapter(subscription));
+                SubscriptionAdapter subscriptionAdapter = new SubscriptionAdapter(subscription);
+                mCs.onSubscribe(mToken, subscriptionAdapter);
+                mSubscription = subscriptionAdapter;
             } catch (RemoteException ex) {
-                ex.rethrowAsRuntimeException();
+                handleRemoteException(ex);
             }
         }
+
         public void onNext(@NonNull Control control) {
             Preconditions.checkNotNull(control);
             try {
@@ -318,20 +323,36 @@
                 }
                 mCs.onNext(mToken, control);
             } catch (RemoteException ex) {
-                ex.rethrowAsRuntimeException();
+                handleRemoteException(ex);
             }
         }
+
         public void onError(Throwable t) {
             try {
                 mCs.onError(mToken, t.toString());
+                mSubscription = null;
             } catch (RemoteException ex) {
-                ex.rethrowAsRuntimeException();
+                handleRemoteException(ex);
             }
         }
+
         public void onComplete() {
             try {
                 mCs.onComplete(mToken);
+                mSubscription = null;
             } catch (RemoteException ex) {
+                handleRemoteException(ex);
+            }
+        }
+
+        private void handleRemoteException(RemoteException ex) {
+            if (ex instanceof DeadObjectException) {
+                // System UI crashed or is restarting. There is no need to rethrow this
+                SubscriptionAdapter subscriptionAdapter = mSubscription;
+                if (subscriptionAdapter != null) {
+                    subscriptionAdapter.cancel();
+                }
+            } else {
                 ex.rethrowAsRuntimeException();
             }
         }
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 9ed57c3..7748d5b 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -23,6 +23,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.text.LineBreakConfig;
 import android.text.style.ParagraphStyle;
 
 /**
@@ -53,7 +54,9 @@
      *                line width
      * @param includePad set whether to include extra space beyond font ascent and descent which is
      *                   needed to avoid clipping in some scripts
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth,
             Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
             boolean includePad) {
@@ -79,7 +82,9 @@
      * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
      *                        {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
      *                        not used, {@code outerWidth} is used instead
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth,
             Alignment align, float spacingmult, float spacingadd, BoringLayout.Metrics metrics,
             boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
@@ -111,7 +116,9 @@
      *                              False for keeping the first font's line height. If some glyphs
      *                              requires larger vertical spaces, by passing true to this
      *                              argument, the layout increase the line height to fit all glyphs.
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public static @NonNull BoringLayout make(
             @NonNull CharSequence source, @NonNull TextPaint paint,
             @IntRange(from = 0) int outerWidth,
@@ -247,10 +254,17 @@
      *                line width
      * @param includePad set whether to include extra space beyond font ascent and descent which is
      *                   needed to avoid clipping in some scripts
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public BoringLayout(CharSequence source, TextPaint paint, int outerwidth, Alignment align,
             float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad) {
-        super(source, paint, outerwidth, align, spacingMult, spacingAdd);
+        super(source, paint, outerwidth, align, TextDirectionHeuristics.LTR, spacingMult,
+                spacingAdd, includePad, false /* fallbackLineSpacing */,
+                outerwidth /* ellipsizedWidth */, null /* ellipsize */, 1 /* maxLines */,
+                BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
+                null /* rightIndents */, JUSTIFICATION_MODE_NONE,
+                LineBreakConfig.NONE);
 
         mEllipsizedWidth = outerwidth;
         mEllipsizedStart = 0;
@@ -277,7 +291,9 @@
      * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
      *                        {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
      *                        not used, {@code outerWidth} is used instead
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align,
             float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad,
             TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
@@ -306,7 +322,9 @@
      *                              False for keeping the first font's line height. If some glyphs
      *                              requires larger vertical spaces, by passing true to this
      *                              argument, the layout increase the line height to fit all glyphs.
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public BoringLayout(
             @NonNull CharSequence source, @NonNull TextPaint paint,
             @IntRange(from = 0) int outerWidth, @NonNull Alignment align, float spacingMult,
@@ -318,25 +336,58 @@
          * but we can't use "this" for the callback until the call to
          * super() finishes.
          */
-        super(source, paint, outerWidth, align, spacingMult, spacingAdd);
+        this(source, paint, outerWidth, align, TextDirectionHeuristics.LTR, spacingMult,
+                spacingAdd, includePad, useFallbackLineSpacing,
+                ellipsizedWidth, ellipsize, 1 /* maxLines */,
+                BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
+                null /* rightIndents */, JUSTIFICATION_MODE_NONE,
+                LineBreakConfig.NONE, metrics);
+    }
+
+    /* package */ BoringLayout(
+            CharSequence text,
+            TextPaint paint,
+            int width,
+            Alignment align,
+            TextDirectionHeuristic textDir,
+            float spacingMult,
+            float spacingAdd,
+            boolean includePad,
+            boolean fallbackLineSpacing,
+            int ellipsizedWidth,
+            TextUtils.TruncateAt ellipsize,
+            int maxLines,
+            int breakStrategy,
+            int hyphenationFrequency,
+            int[] leftIndents,
+            int[] rightIndents,
+            int justificationMode,
+            LineBreakConfig lineBreakConfig,
+            Metrics metrics) {
+
+        super(text, paint, width, align, textDir, spacingMult, spacingAdd, includePad,
+                fallbackLineSpacing, ellipsizedWidth, ellipsize, maxLines, breakStrategy,
+                hyphenationFrequency, leftIndents, rightIndents, justificationMode,
+                lineBreakConfig);
+
 
         boolean trust;
 
         if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
-            mEllipsizedWidth = outerWidth;
+            mEllipsizedWidth = width;
             mEllipsizedStart = 0;
             mEllipsizedCount = 0;
             trust = true;
         } else {
-            replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
-                        paint, outerWidth, align, spacingMult, spacingAdd);
+            replaceWith(TextUtils.ellipsize(text, paint, ellipsizedWidth, ellipsize, true, this),
+                        paint, width, align, spacingMult, spacingAdd);
 
             mEllipsizedWidth = ellipsizedWidth;
             trust = false;
         }
 
-        mUseFallbackLineSpacing = useFallbackLineSpacing;
-        init(getText(), paint, align, metrics, includePad, trust, useFallbackLineSpacing);
+        mUseFallbackLineSpacing = fallbackLineSpacing;
+        init(getText(), paint, align, metrics, includePad, trust, fallbackLineSpacing);
     }
 
     /* package */ void init(CharSequence source, TextPaint paint, Alignment align,
@@ -391,7 +442,9 @@
      * @param paint a paint
      * @return layout metric for the given text. null if given text is unable to be handled by
      *         BoringLayout.
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public static Metrics isBoring(CharSequence text, TextPaint paint) {
         return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null);
     }
@@ -406,7 +459,9 @@
      * @return layout metric for the given text. If metrics is not null, this method fills values
      *         to given metrics object instead of allocating new metrics object. null if given text
      *         is unable to be handled by BoringLayout.
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) {
         return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics);
     }
@@ -466,7 +521,9 @@
      *                              argument, the layout increase the line height to fit all glyphs.
      * @param metrics the out metrics.
      * @return metrics on success. null if text cannot be rendered by BoringLayout.
+     * @deprecated Use {@link android.text.Layout.Builder} instead.
      */
+    @Deprecated
     public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint,
             @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing,
             @Nullable Metrics metrics) {
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index ba08f25..3bdaca9 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -388,7 +388,11 @@
                          @Nullable TextUtils.TruncateAt ellipsize,
                          @IntRange(from = 0) int ellipsizedWidth) {
         super(createEllipsizer(ellipsize, display),
-              paint, width, align, textDir, spacingmult, spacingadd);
+              paint, width, align, textDir, spacingmult, spacingadd, includepad,
+                false /* fallbackLineSpacing */, ellipsizedWidth, ellipsize,
+                Integer.MAX_VALUE /* maxLines */, breakStrategy, hyphenationFrequency,
+                null /* leftIndents */, null /* rightIndents */, justificationMode,
+                lineBreakConfig);
 
         final Builder b = Builder.obtain(base, paint, width)
                 .setAlignment(align)
@@ -410,7 +414,11 @@
 
     private DynamicLayout(@NonNull Builder b) {
         super(createEllipsizer(b.mEllipsize, b.mDisplay),
-                b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd);
+                b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd,
+                b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
+                Integer.MAX_VALUE /* maxLines */, b.mBreakStrategy, b.mHyphenationFrequency,
+                null /* leftIndents */, null /* rightIndents */, b.mJustificationMode,
+                b.mLineBreakConfig);
 
         mDisplay = b.mDisplay;
         mIncludePad = b.mIncludePad;
@@ -615,7 +623,6 @@
         }
 
         if (reflowed == null) {
-            reflowed = new StaticLayout(null);
             b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth());
         }
 
@@ -631,9 +638,10 @@
                 .setHyphenationFrequency(mHyphenationFrequency)
                 .setJustificationMode(mJustificationMode)
                 .setLineBreakConfig(mLineBreakConfig)
-                .setAddLastLineLineSpacing(!islast);
+                .setAddLastLineLineSpacing(!islast)
+                .setIncludePad(false);
 
-        reflowed.generate(b, false /*includepad*/, true /*trackpad*/);
+        reflowed = b.regenerate(true /* trackpadding */, reflowed);
         int n = reflowed.getLineCount();
         // If the new layout has a blank line at the end, but it is not
         // the very end of the buffer, then we already have a line that
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 64dc16d..98b9fee 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -26,6 +26,7 @@
 import android.graphics.Path;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.text.LineBreakConfig;
 import android.graphics.text.LineBreaker;
 import android.os.Build;
 import android.text.method.TextKeyListener;
@@ -278,7 +279,9 @@
                      int width, Alignment align,
                      float spacingMult, float spacingAdd) {
         this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
-                spacingMult, spacingAdd);
+                spacingMult, spacingAdd, false, false, 0, null, Integer.MAX_VALUE,
+                BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null, null,
+                JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE);
     }
 
     /**
@@ -290,15 +293,44 @@
      * @param width the wrapping width for the text.
      * @param align whether to left, right, or center the text.  Styles can
      * override the alignment.
+     * @param textDir a text direction heuristic.
      * @param spacingMult factor by which to scale the font size to get the
      * default line spacing
      * @param spacingAdd amount to add to the default line spacing
+     * @param includePad true for enabling including font padding
+     * @param fallbackLineSpacing true for enabling fallback line spacing
+     * @param ellipsizedWidth width as used for ellipsizing purpose
+     * @param ellipsize an ellipsize option
+     * @param maxLines a maximum number of lines.
+     * @param breakStrategy a break strategy.
+     * @param hyphenationFrequency a hyphenation frequency
+     * @param leftIndents a visually left margins
+     * @param rightIndents a visually right margins
+     * @param justificationMode a justification mode
+     * @param lineBreakConfig a line break config
      *
      * @hide
      */
-    protected Layout(CharSequence text, TextPaint paint,
-                     int width, Alignment align, TextDirectionHeuristic textDir,
-                     float spacingMult, float spacingAdd) {
+    protected Layout(
+            CharSequence text,
+            TextPaint paint,
+            int width,
+            Alignment align,
+            TextDirectionHeuristic textDir,
+            float spacingMult,
+            float spacingAdd,
+            boolean includePad,
+            boolean fallbackLineSpacing,
+            int ellipsizedWidth,
+            TextUtils.TruncateAt ellipsize,
+            int maxLines,
+            int breakStrategy,
+            int hyphenationFrequency,
+            int[] leftIndents,
+            int[] rightIndents,
+            int justificationMode,
+            LineBreakConfig lineBreakConfig
+    ) {
 
         if (width < 0)
             throw new IllegalArgumentException("Layout: " + width + " < 0");
@@ -320,11 +352,17 @@
         mSpacingAdd = spacingAdd;
         mSpannedText = text instanceof Spanned;
         mTextDir = textDir;
-    }
-
-    /** @hide */
-    protected void setJustificationMode(@JustificationMode int justificationMode) {
+        mIncludePad = includePad;
+        mFallbackLineSpacing = fallbackLineSpacing;
+        mEllipsizedWidth = ellipsize == null ? width : ellipsizedWidth;
+        mEllipsize = ellipsize;
+        mMaxLines = maxLines;
+        mBreakStrategy = breakStrategy;
+        mHyphenationFrequency = hyphenationFrequency;
+        mLeftIndents = leftIndents;
+        mRightIndents = rightIndents;
         mJustificationMode = justificationMode;
+        mLineBreakConfig = lineBreakConfig;
     }
 
     /**
@@ -910,37 +948,6 @@
     }
 
     /**
-     * Return the text that is displayed by this Layout.
-     */
-    public final CharSequence getText() {
-        return mText;
-    }
-
-    /**
-     * Return the base Paint properties for this layout.
-     * Do NOT change the paint, which may result in funny
-     * drawing for this layout.
-     */
-    public final TextPaint getPaint() {
-        return mPaint;
-    }
-
-    /**
-     * Return the width of this layout.
-     */
-    public final int getWidth() {
-        return mWidth;
-    }
-
-    /**
-     * Return the width to which this Layout is ellipsizing, or
-     * {@link #getWidth} if it is not doing anything special.
-     */
-    public int getEllipsizedWidth() {
-        return mWidth;
-    }
-
-    /**
      * Increase the width of this layout to the specified width.
      * Be careful to use this only when you know it is appropriate&mdash;
      * it does not cause the text to reflow to use the full new width.
@@ -972,35 +979,6 @@
     }
 
     /**
-     * Return the base alignment of this layout.
-     */
-    public final Alignment getAlignment() {
-        return mAlignment;
-    }
-
-    /**
-     * Return what the text height is multiplied by to get the line height.
-     */
-    public final float getSpacingMultiplier() {
-        return mSpacingMult;
-    }
-
-    /**
-     * Return the number of units of leading that are added to each line.
-     */
-    public final float getSpacingAdd() {
-        return mSpacingAdd;
-    }
-
-    /**
-     * Return the heuristic used to determine paragraph text direction.
-     * @hide
-     */
-    public final TextDirectionHeuristic getTextDirectionHeuristic() {
-        return mTextDir;
-    }
-
-    /**
      * Return the number of lines of text in this layout.
      */
     public abstract int getLineCount();
@@ -1105,15 +1083,6 @@
     }
 
     /**
-     * Return true if the fallback line space is enabled in this Layout.
-     *
-     * @return true if the fallback line space is enabled. Otherwise returns false.
-     */
-    public boolean isFallbackLineSpacingEnabled() {
-        return false;
-    }
-
-    /**
      * Returns true if the character at offset and the preceding character
      * are at different run levels (and thus there's a split caret).
      * @param offset the offset
@@ -3254,7 +3223,17 @@
     private boolean mSpannedText;
     private TextDirectionHeuristic mTextDir;
     private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
+    private boolean mIncludePad;
+    private boolean mFallbackLineSpacing;
+    private int mEllipsizedWidth;
+    private TextUtils.TruncateAt mEllipsize;
+    private int mMaxLines;
+    private int mBreakStrategy;
+    private int mHyphenationFrequency;
+    private int[] mLeftIndents;
+    private int[] mRightIndents;
     private int mJustificationMode;
+    private LineBreakConfig mLineBreakConfig;
 
     /** @hide */
     @IntDef(prefix = { "DIR_" }, value = {
@@ -3352,4 +3331,683 @@
          */
         boolean isSegmentInside(@NonNull RectF segmentBounds, @NonNull RectF area);
     }
+
+    /**
+     * A builder class for Layout object.
+     *
+     * Different from {@link StaticLayout.Builder}, this builder generates the optimal layout based
+     * on input. If the given text and parameters can be rendered with {@link BoringLayout}, this
+     * builder generates {@link BoringLayout} instance. Otherwise, {@link StaticLayout} instance is
+     * generated.
+     *
+     * @see StaticLayout.Builder
+     */
+    public static final class Builder {
+        /**
+         * Construct a builder class.
+         *
+         * @param text a text to be displayed.
+         * @param start an inclusive start index of the text to be displayed.
+         * @param end an exclusive end index of the text to be displayed.
+         * @param paint a paint object to be used for drawing text.
+         * @param width a width constraint in pixels.
+         */
+        public Builder(
+                @NonNull CharSequence text,
+                @IntRange(from = 0) int start,
+                @IntRange(from = 0) int end,
+                @NonNull TextPaint paint,
+                @IntRange(from = 0) int width) {
+            mText = text;
+            mStart = start;
+            mEnd = end;
+            mPaint = paint;
+            mWidth = width;
+            mEllipsizedWidth = width;
+        }
+
+        /**
+         * Set the text alignment.
+         *
+         * The default value is {@link Layout.Alignment#ALIGN_NORMAL}.
+         *
+         * @param alignment an alignment.
+         * @return this builder instance.
+         * @see Layout.Alignment
+         * @see Layout#getAlignment()
+         * @see StaticLayout.Builder#setAlignment(Alignment)
+         */
+        @NonNull
+        public Builder setAlignment(@NonNull Alignment alignment) {
+            mAlignment = alignment;
+            return this;
+        }
+
+        /**
+         * Set the text direction heuristics.
+         *
+         * The text direction heuristics is used to resolve text direction on the text.
+         *
+         * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}
+         *
+         * @param textDirection a text direction heuristic.
+         * @return this builder instance.
+         * @see TextDirectionHeuristics
+         * @see Layout#getTextDirectionHeuristic()
+         * @see StaticLayout.Builder#setTextDirection(TextDirectionHeuristic)
+         */
+        @NonNull
+        public Builder setTextDirectionHeuristic(@NonNull TextDirectionHeuristic textDirection) {
+            mTextDir = textDirection;
+            return this;
+        }
+
+        /**
+         * Set the line spacing amount.
+         *
+         * The specified amount of pixels will be added to each line.
+         *
+         * The default value is {@code 0}.
+         *
+         * @param amount an amount of pixels to be added to line height.
+         * @return this builder instance.
+         * @see Layout#getLineSpacingAmount()
+         * @see Layout#getSpacingAdd()
+         * @see StaticLayout.Builder#setLineSpacing(float, float)
+         */
+        @NonNull
+        public Builder setLineSpacingAmount(float amount) {
+            mSpacingAdd = amount;
+            return this;
+        }
+
+        /**
+         * Set the line spacing multiplier.
+         *
+         * The specified value will be multiplied to each line.
+         *
+         * The default value is {@code 1}.
+         *
+         * @param multiplier a multiplier to be applied to the line height
+         * @return this builder instance.
+         * @see Layout#getLineSpacingMultiplier()
+         * @see Layout#getSpacingMultiplier()
+         * @see StaticLayout.Builder#setLineSpacing(float, float)
+         */
+        @NonNull
+        public Builder setLineSpacingMultiplier(float multiplier) {
+            mSpacingMult = multiplier;
+            return this;
+        }
+
+        /**
+         * Set whether including extra padding into the first and the last line height.
+         *
+         * By setting true, the first line of the text and the last line of the text will have extra
+         * vertical space for avoiding clipping.
+         *
+         * The default value is {@code true}.
+         *
+         * @param includeFontPadding true for including extra space into first and last line.
+         * @return this builder instance.
+         * @see Layout#isIncludeFontPadding()
+         * @see StaticLayout.Builder#setIncludePad(boolean)
+         */
+        @NonNull
+        public Builder setIncludeFontPadding(boolean includeFontPadding) {
+            mIncludePad = includeFontPadding;
+            return this;
+        }
+
+        /**
+         * Set whether to respect the ascent and descent of the fallback fonts.
+         *
+         * Set whether to respect the ascent and descent of the fallback fonts that are used in
+         * displaying the text (which is needed to avoid text from consecutive lines running into
+         * each other). If set, fallback fonts that end up getting used can increase the ascent
+         * and descent of the lines that they are used on.
+         *
+         * The default value is {@code false}
+         *
+         * @param fallbackLineSpacing whether to expand line height based on fallback fonts.
+         * @return this builder instance.
+         * @see Layout#isFallbackLineSpacingEnabled()
+         * @see StaticLayout.Builder#setUseLineSpacingFromFallbacks(boolean)
+         */
+        @NonNull
+        public Builder setFallbackLineSpacingEnabled(boolean fallbackLineSpacing) {
+            mFallbackLineSpacing = fallbackLineSpacing;
+            return this;
+        }
+
+        /**
+         * Set the width as used for ellipsizing purpose in pixels.
+         *
+         * The passed value is ignored and forced to set to the value of width constraint passed in
+         * constructor if no ellipsize option is set.
+         *
+         * The default value is the width constraint.
+         *
+         * @param ellipsizeWidth a ellipsizing width in pixels.
+         * @return this builder instance.
+         * @see Layout#getEllipsizedWidth()
+         * @see StaticLayout.Builder#setEllipsizedWidth(int)
+         */
+        @NonNull
+        public Builder setEllipsizedWidth(@IntRange(from = 0) int ellipsizeWidth) {
+            mEllipsizedWidth = ellipsizeWidth;
+            return this;
+        }
+
+        /**
+         * Set the ellipsizing type.
+         *
+         * By setting null, the ellipsize is disabled.
+         *
+         * The default value is {@code null}.
+         *
+         * @param ellipsize type of the ellipsize. null for disabling ellipsize.
+         * @return this builder instance.
+         * @see Layout#getEllipsize()
+         * @see StaticLayout.Builder#getEllipsize()
+         * @see android.text.TextUtils.TruncateAt
+         */
+        @NonNull
+        public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
+            mEllipsize = ellipsize;
+            return this;
+        }
+
+        /**
+         * Set the maximum number of lines.
+         *
+         * The default value is unlimited.
+         *
+         * @param maxLines maximum number of lines in the layout.
+         * @return this builder instance.
+         * @see Layout#getMaxLines()
+         * @see StaticLayout.Builder#setMaxLines(int)
+         */
+        @NonNull
+        public Builder setMaxLines(@IntRange(from = 1) int maxLines) {
+            mMaxLines = maxLines;
+            return this;
+        }
+
+        /**
+         * Set the line break strategy.
+         *
+         * The default value is {@link Layout#BREAK_STRATEGY_SIMPLE}.
+         *
+         * @param breakStrategy a break strategy for line breaking.
+         * @return this builder instance.
+         * @see Layout#getBreakStrategy()
+         * @see StaticLayout.Builder#setBreakStrategy(int)
+         * @see Layout#BREAK_STRATEGY_SIMPLE
+         * @see Layout#BREAK_STRATEGY_HIGH_QUALITY
+         * @see Layout#BREAK_STRATEGY_BALANCED
+         */
+        @NonNull
+        public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
+            mBreakStrategy = breakStrategy;
+            return this;
+        }
+
+        /**
+         * Set the hyphenation frequency.
+         *
+         * The default value is {@link Layout#HYPHENATION_FREQUENCY_NONE}.
+         *
+         * @param hyphenationFrequency a hyphenation frequency.
+         * @return this builder instance.
+         * @see Layout#getHyphenationFrequency()
+         * @see StaticLayout.Builder#setHyphenationFrequency(int)
+         * @see Layout#HYPHENATION_FREQUENCY_NONE
+         * @see Layout#HYPHENATION_FREQUENCY_NORMAL
+         * @see Layout#HYPHENATION_FREQUENCY_FULL
+         * @see Layout#HYPHENATION_FREQUENCY_NORMAL_FAST
+         * @see Layout#HYPHENATION_FREQUENCY_FULL_FAST
+         */
+        @NonNull
+        public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
+            mHyphenationFrequency = hyphenationFrequency;
+            return this;
+        }
+
+        /**
+         * Set visually left indents in pixels per lines.
+         *
+         * For the lines past the last element in the array, the last element repeats. Passing null
+         * for disabling indents.
+         *
+         * Note that even with the RTL layout, this method reserve spacing at the visually left of
+         * the line.
+         *
+         * The default value is {@code null}.
+         *
+         * @param leftIndents array of indents values for the left margins in pixels.
+         * @return this builder instance.
+         * @see Layout#getLeftIndents()
+         * @see Layout#getRightIndents()
+         * @see Layout.Builder#setRightIndents(int[])
+         * @see StaticLayout.Builder#setIndents(int[], int[])
+         */
+        @NonNull
+        public Builder setLeftIndents(@Nullable int[] leftIndents) {
+            mLeftIndents = leftIndents;
+            return this;
+        }
+
+        /**
+         * Set visually right indents in pixels per lines.
+         *
+         * For the lines past the last element in the array, the last element repeats. Passing null
+         * for disabling indents.
+         *
+         * Note that even with the RTL layout, this method reserve spacing at the visually right of
+         * the line.
+         *
+         * The default value is {@code null}.
+         *
+         * @param rightIndents array of indents values for the right margins in pixels.
+         * @return this builder instance.
+         * @see Layout#getLeftIndents()
+         * @see Layout#getRightIndents()
+         * @see Layout.Builder#setLeftIndents(int[])
+         * @see StaticLayout.Builder#setIndents(int[], int[])
+         */
+        @NonNull
+        public Builder setRightIndents(@Nullable int[] rightIndents) {
+            mRightIndents = rightIndents;
+            return this;
+        }
+
+        /**
+         * Set justification mode.
+         *
+         * When justification mode is {@link Layout#JUSTIFICATION_MODE_INTER_WORD}, the word spacing
+         * on the given Paint passed to the constructor will be ignored. This behavior also affects
+         * spans which change the word spacing.
+         *
+         * The default value is {@link Layout#JUSTIFICATION_MODE_NONE}.
+         *
+         * @param justificationMode justification mode.
+         * @return this builder instance.
+         * @see Layout#getJustificationMode()
+         * @see StaticLayout.Builder#setJustificationMode(int)
+         * @see Layout#JUSTIFICATION_MODE_NONE
+         * @see Layout#JUSTIFICATION_MODE_INTER_WORD
+         */
+        @NonNull
+        public Builder setJustificationMode(@JustificationMode int justificationMode) {
+            mJustificationMode = justificationMode;
+            return this;
+        }
+
+        /**
+         * Set the line break configuration.
+         *
+         * The default value is a LinebreakConfig instance that has
+         * {@link LineBreakConfig#LINE_BREAK_STYLE_NONE} and
+         * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE}.
+         *
+         * @param lineBreakConfig the line break configuration
+         * @return this builder instance.
+         * @see Layout#getLineBreakConfig()
+         * @see StaticLayout.Builder#setLineBreakConfig(LineBreakConfig)
+         */
+        @NonNull
+        public Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
+            mLineBreakConfig = lineBreakConfig;
+            return this;
+        }
+
+        private BoringLayout.Metrics isBoring() {
+            if (mStart != 0 || mEnd != mText.length()) {  // BoringLayout only support entire text.
+                return null;
+            }
+            BoringLayout.Metrics metrics = BoringLayout.isBoring(mText, mPaint, mTextDir,
+                    mFallbackLineSpacing, null);
+            if (metrics == null) {
+                return null;
+            }
+            if (metrics.width <= mWidth) {
+                return metrics;
+            }
+            if (mEllipsize != null) {
+                return metrics;
+            }
+            return null;
+        }
+
+        /**
+         * Build a Layout object.
+         */
+        @NonNull
+        public Layout build() {
+            BoringLayout.Metrics metrics = isBoring();
+            if (metrics == null) {  // we cannot use BoringLayout, create StaticLayout.
+                return StaticLayout.Builder.obtain(mText, mStart, mEnd, mPaint, mWidth)
+                        .setAlignment(mAlignment)
+                        .setLineSpacing(mSpacingAdd, mSpacingMult)
+                        .setTextDirection(mTextDir)
+                        .setIncludePad(mIncludePad)
+                        .setUseLineSpacingFromFallbacks(mFallbackLineSpacing)
+                        .setEllipsizedWidth(mEllipsizedWidth)
+                        .setEllipsize(mEllipsize)
+                        .setMaxLines(mMaxLines)
+                        .setBreakStrategy(mBreakStrategy)
+                        .setHyphenationFrequency(mHyphenationFrequency)
+                        .setIndents(mLeftIndents, mRightIndents)
+                        .setJustificationMode(mJustificationMode)
+                        .setLineBreakConfig(mLineBreakConfig)
+                        .build();
+            } else {
+                return new BoringLayout(
+                        mText, mPaint, mWidth, mAlignment, mTextDir, mSpacingMult, mSpacingAdd,
+                        mIncludePad, mFallbackLineSpacing, mEllipsizedWidth, mEllipsize, mMaxLines,
+                        mBreakStrategy, mHyphenationFrequency, mLeftIndents, mRightIndents,
+                        mJustificationMode, mLineBreakConfig, metrics);
+            }
+        }
+
+        private final CharSequence mText;
+        private final int mStart;
+        private final int mEnd;
+        private final TextPaint mPaint;
+        private final int mWidth;
+        private Alignment mAlignment = Alignment.ALIGN_NORMAL;
+        private float mSpacingMult = 1.0f;
+        private float mSpacingAdd = 0.0f;
+        private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
+        private boolean mIncludePad = true;
+        private boolean mFallbackLineSpacing = false;
+        private int mEllipsizedWidth;
+        private TextUtils.TruncateAt mEllipsize = null;
+        private int mMaxLines = Integer.MAX_VALUE;
+        private int mBreakStrategy = BREAK_STRATEGY_SIMPLE;
+        private int mHyphenationFrequency = HYPHENATION_FREQUENCY_NONE;
+        private int[] mLeftIndents = null;
+        private int[] mRightIndents = null;
+        private int mJustificationMode = JUSTIFICATION_MODE_NONE;
+        private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Getters of parameters that is used for building Layout instance
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Return the text used for creating this layout.
+     *
+     * @return the text used for creating this layout.
+     * @see Layout.Builder
+     */
+    @NonNull
+    public final CharSequence getText() {
+        return mText;
+    }
+
+    /**
+     * Return the paint used for creating this layout.
+     *
+     * Do not modify the returned paint object. This paint object will still be used for
+     * drawing/measuring text.
+     *
+     * @return the paint used for creating this layout.
+     * @see Layout.Builder
+     */
+    @NonNull
+    public final TextPaint getPaint() {
+        return mPaint;
+    }
+
+    /**
+     * Return the width used for creating this layout in pixels.
+     *
+     * @return the width used for creating this layout in pixels.
+     * @see Layout.Builder
+     */
+    @IntRange(from = 0)
+    public final int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * Returns the alignment used for creating this layout in pixels.
+     *
+     * @return the alignment used for creating this layout.
+     * @see Layout.Builder#setAlignment(Alignment)
+     * @see StaticLayout.Builder#setAlignment(Alignment)
+     */
+    @NonNull
+    public final Alignment getAlignment() {
+        return mAlignment;
+    }
+
+    /**
+     * Returns the text direction heuristic used for creating this layout.
+     *
+     * @return the text direction heuristic used for creating this layout
+     * @see Layout.Builder#setTextDirectionHeuristic(TextDirectionHeuristic)
+     * @see StaticLayout.Builder#setTextDirection(TextDirectionHeuristic)
+     */
+    @NonNull
+    public final TextDirectionHeuristic getTextDirectionHeuristic() {
+        return mTextDir;
+    }
+
+    /**
+     * Returns the multiplier applied to the line height.
+     *
+     * This is an alias of {@link #getLineSpacingMultiplier}.
+     *
+     * @return the line height multiplier.
+     * @see Layout.Builder#setLineSpacingMultiplier(float)
+     * @see StaticLayout.Builder#setLineSpacing(float, float)
+     * @see Layout#getLineSpacingMultiplier()
+     */
+    public final float getSpacingMultiplier() {
+        return getLineSpacingMultiplier();
+    }
+
+    /**
+     * Returns the multiplier applied to the line height.
+     *
+     * @return the line height multiplier.
+     * @see Layout.Builder#setLineSpacingMultiplier(float)
+     * @see StaticLayout.Builder#setLineSpacing(float, float)
+     * @see Layout#getSpacingMultiplier()
+     */
+    public final float getLineSpacingMultiplier() {
+        return mSpacingMult;
+    }
+
+    /**
+     * Returns the amount added to the line height.
+     *
+     * This is an alias of {@link #getLineSpacingAmount()}.
+     *
+     * @return the line height additional amount.
+     * @see Layout.Builder#setLineSpacingAmount(float)
+     * @see StaticLayout.Builder#setLineSpacing(float, float)
+     * @see Layout#getLineSpacingAmount()
+     */
+    public final float getSpacingAdd() {
+        return getLineSpacingAmount();
+    }
+
+    /**
+     * Returns the amount added to the line height.
+     *
+     * @return the line height additional amount.
+     * @see Layout.Builder#setLineSpacingAmount(float)
+     * @see StaticLayout.Builder#setLineSpacing(float, float)
+     * @see Layout#getSpacingAdd()
+     */
+    public final float getLineSpacingAmount() {
+        return mSpacingAdd;
+    }
+
+    /**
+     * Returns true if this layout is created with increased line height.
+     *
+     * @return true if the layout is created with increased line height.
+     * @see Layout.Builder#setIncludeFontPadding(boolean)
+     * @see StaticLayout.Builder#setIncludePad(boolean)
+     */
+    public final boolean isIncludeFontPadding() {
+        return mIncludePad;
+    }
+
+    /**
+     * Return true if the fallback line space is enabled in this Layout.
+     *
+     * @return true if the fallback line space is enabled. Otherwise, returns false.
+     * @see Layout.Builder#setFallbackLineSpacingEnabled(boolean)
+     * @see StaticLayout.Builder#setUseLineSpacingFromFallbacks(boolean)
+     */
+    // not being final because of already published API.
+    public boolean isFallbackLineSpacingEnabled() {
+        return mFallbackLineSpacing;
+    }
+
+    /**
+     * Return the width to which this layout is ellipsized.
+     *
+     * If no ellipsize is applied, the same amount of {@link #getWidth} is returned.
+     *
+     * @return the amount of ellipsized width in pixels.
+     * @see Layout.Builder#setEllipsizedWidth(int)
+     * @see StaticLayout.Builder#setEllipsizedWidth(int)
+     * @see Layout.Builder#setEllipsize(TextUtils.TruncateAt)
+     * @see StaticLayout.Builder#setEllipsize(TextUtils.TruncateAt)
+     * @see Layout#getEllipsize()
+     */
+    @IntRange(from = 0)
+    public int getEllipsizedWidth() {  // not being final because of already published API.
+        return mEllipsizedWidth;
+    }
+
+    /**
+     * Return the ellipsize option used for creating this layout.
+     *
+     * May return null if no ellipsize option was selected.
+     *
+     * @return The ellipsize option used for creating this layout, or null if no ellipsize option
+     * was selected.
+     * @see Layout.Builder#setEllipsize(TextUtils.TruncateAt)
+     * @see StaticLayout.Builder#setEllipsize(TextUtils.TruncateAt)
+     * @see Layout.Builder#setEllipsizedWidth(int)
+     * @see StaticLayout.Builder#setEllipsizedWidth(int)
+     * @see Layout#getEllipsizedWidth()
+     */
+    @Nullable
+    public final TextUtils.TruncateAt getEllipsize() {
+        return mEllipsize;
+    }
+
+    /**
+     * Return the maximum lines allowed used for creating this layout.
+     *
+     * Note that this is not an actual line count of this layout. Use {@link #getLineCount()} for
+     * getting the actual line count of this layout.
+     *
+     * @return the maximum lines allowed used for creating this layout.
+     * @see Layout.Builder#setMaxLines(int)
+     * @see StaticLayout.Builder#setMaxLines(int)
+     */
+    @IntRange(from = 1)
+    public final int getMaxLines() {
+        return mMaxLines;
+    }
+
+    /**
+     * Return the break strategy used for creating this layout.
+     *
+     * @return the break strategy used for creating this layout.
+     * @see Layout.Builder#setBreakStrategy(int)
+     * @see StaticLayout.Builder#setBreakStrategy(int)
+     */
+    @BreakStrategy
+    public final int getBreakStrategy() {
+        return mBreakStrategy;
+    }
+
+    /**
+     * Return the hyphenation frequency used for creating this layout.
+     *
+     * @return the hyphenation frequency used for creating this layout.
+     * @see Layout.Builder#setHyphenationFrequency(int)
+     * @see StaticLayout.Builder#setHyphenationFrequency(int)
+     */
+    @HyphenationFrequency
+    public final int getHyphenationFrequency() {
+        return mHyphenationFrequency;
+    }
+
+    /**
+     * Return a copy of the left indents used for this layout.
+     *
+     * May return null if no left indentation is applied.
+     *
+     * @return the array of left indents in pixels.
+     * @see Layout.Builder#setLeftIndents(int[])
+     * @see Layout.Builder#setRightIndents(int[])
+     * @see StaticLayout.Builder#setIndents(int[], int[])
+     */
+    @Nullable
+    public final int[] getLeftIndents() {
+        if (mLeftIndents == null) {
+            return null;
+        }
+        int[] newArray = new int[mLeftIndents.length];
+        System.arraycopy(mLeftIndents, 0, newArray, 0, newArray.length);
+        return newArray;
+    }
+
+    /**
+     * Return a copy of the right indents used for this layout.
+     *
+     * May return null if no right indentation is applied.
+     *
+     * @return the array of right indents in pixels.
+     * @see Layout.Builder#setLeftIndents(int[])
+     * @see Layout.Builder#setRightIndents(int[])
+     * @see StaticLayout.Builder#setIndents(int[], int[])
+     */
+    @Nullable
+    public final int[] getRightIndents() {
+        if (mRightIndents == null) {
+            return mLeftIndents;
+        }
+        int[] newArray = new int[mRightIndents.length];
+        System.arraycopy(mRightIndents, 0, newArray, 0, newArray.length);
+        return newArray;
+    }
+
+    /**
+     * Return the justification mode used for creating this layout.
+     *
+     * @return the justification mode used for creating this layout.
+     * @see Layout.Builder#setJustificationMode(int)
+     * @see StaticLayout.Builder#setJustificationMode(int)
+     */
+    @JustificationMode
+    public final int getJustificationMode() {
+        return mJustificationMode;
+    }
+
+    /**
+     * Gets the {@link LineBreakConfig} used for creating this layout.
+     *
+     * Do not modify the returned object.
+     *
+     * @return The line break config used for creating this layout.
+     */
+    // not being final because of subclass has already published API.
+    @NonNull
+    public LineBreakConfig getLineBreakConfig() {
+        return mLineBreakConfig;
+    }
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index ab9cff0..f843900 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -431,11 +431,21 @@
          */
         @NonNull
         public StaticLayout build() {
-            StaticLayout result = new StaticLayout(this);
+            StaticLayout result = new StaticLayout(this, mIncludePad, mEllipsize != null
+                    ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL);
             Builder.recycle(this);
             return result;
         }
 
+        /* package */ @NonNull StaticLayout regenerate(boolean trackpadding, StaticLayout recycle) {
+            if (recycle == null) {
+                return new StaticLayout(this, trackpadding, COLUMNS_ELLIPSIZE);
+            } else {
+                recycle.generate(this, mIncludePad, trackpadding);
+                return recycle;
+            }
+        }
+
         private CharSequence mText;
         private int mStart;
         private int mEnd;
@@ -515,88 +525,32 @@
                         float spacingmult, float spacingadd,
                         boolean includepad,
                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
-        super((ellipsize == null)
-                ? source
-                : (source instanceof Spanned)
-                    ? new SpannedEllipsizer(source)
-                    : new Ellipsizer(source),
-              paint, outerwidth, align, textDir, spacingmult, spacingadd);
-
-        Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
-            .setAlignment(align)
-            .setTextDirection(textDir)
-            .setLineSpacing(spacingadd, spacingmult)
-            .setIncludePad(includepad)
-            .setEllipsizedWidth(ellipsizedWidth)
-            .setEllipsize(ellipsize)
-            .setMaxLines(maxLines);
-        /*
-         * This is annoying, but we can't refer to the layout until superclass construction is
-         * finished, and the superclass constructor wants the reference to the display text.
-         *
-         * In other words, the two Ellipsizer classes in Layout.java need a (Dynamic|Static)Layout
-         * as a parameter to do their calculations, but the Ellipsizers also need to be the input
-         * to the superclass's constructor (Layout). In order to go around the circular
-         * dependency, we construct the Ellipsizer with only one of the parameters, the text. And
-         * we fill in the rest of the needed information (layout, width, and method) later, here.
-         *
-         * This will break if the superclass constructor ever actually cares about the content
-         * instead of just holding the reference.
-         */
-        if (ellipsize != null) {
-            Ellipsizer e = (Ellipsizer) getText();
-
-            e.mLayout = this;
-            e.mWidth = ellipsizedWidth;
-            e.mMethod = ellipsize;
-            mEllipsizedWidth = ellipsizedWidth;
-
-            mColumns = COLUMNS_ELLIPSIZE;
-        } else {
-            mColumns = COLUMNS_NORMAL;
-            mEllipsizedWidth = outerwidth;
-        }
-
-        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2);
-        mLines  = ArrayUtils.newUnpaddedIntArray(2 * mColumns);
-        mMaximumVisibleLineCount = maxLines;
-
-        generate(b, b.mIncludePad, b.mIncludePad);
-
-        Builder.recycle(b);
+        this(Builder.obtain(source, bufstart, bufend, paint, outerwidth)
+                .setAlignment(align)
+                .setTextDirection(textDir)
+                .setLineSpacing(spacingadd, spacingmult)
+                .setIncludePad(includepad)
+                .setEllipsize(ellipsize)
+                .setEllipsizedWidth(ellipsizedWidth)
+                .setMaxLines(maxLines), includepad,
+                ellipsize != null ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL);
     }
 
-    /**
-     * Used by DynamicLayout.
-     */
-    /* package */ StaticLayout(@Nullable CharSequence text) {
-        super(text, null, 0, null, 0, 0);
+    private StaticLayout(Builder b, boolean trackPadding, int columnSize) {
+        super((b.mEllipsize == null) ? b.mText : (b.mText instanceof Spanned)
+                    ? new SpannedEllipsizer(b.mText) : new Ellipsizer(b.mText),
+                b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd,
+                b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
+                b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents,
+                b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig);
 
-        mColumns = COLUMNS_ELLIPSIZE;
-        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2);
-        mLines  = ArrayUtils.newUnpaddedIntArray(2 * mColumns);
-    }
-
-    private StaticLayout(Builder b) {
-        super((b.mEllipsize == null)
-                ? b.mText
-                : (b.mText instanceof Spanned)
-                    ? new SpannedEllipsizer(b.mText)
-                    : new Ellipsizer(b.mText),
-                b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd);
-
+        mColumns = columnSize;
         if (b.mEllipsize != null) {
             Ellipsizer e = (Ellipsizer) getText();
 
             e.mLayout = this;
             e.mWidth = b.mEllipsizedWidth;
             e.mMethod = b.mEllipsize;
-            mEllipsizedWidth = b.mEllipsizedWidth;
-
-            mColumns = COLUMNS_ELLIPSIZE;
-        } else {
-            mColumns = COLUMNS_NORMAL;
-            mEllipsizedWidth = b.mWidth;
         }
 
         mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2);
@@ -605,9 +559,8 @@
 
         mLeftIndents = b.mLeftIndents;
         mRightIndents = b.mRightIndents;
-        setJustificationMode(b.mJustificationMode);
 
-        generate(b, b.mIncludePad, b.mIncludePad);
+        generate(b, b.mIncludePad, trackPadding);
     }
 
     private static int getBaseHyphenationFrequency(int frequency) {
@@ -648,7 +601,7 @@
         mLineCount = 0;
         mEllipsized = false;
         mMaxLineHeight = mMaximumVisibleLineCount < 1 ? 0 : DEFAULT_MAX_LINE_HEIGHT;
-        mFallbackLineSpacing = b.mFallbackLineSpacing;
+        boolean isFallbackLineSpacing = b.mFallbackLineSpacing;
 
         int v = 0;
         boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
@@ -887,17 +840,17 @@
 
                     boolean moreChars = (endPos < bufEnd);
 
-                    final int ascent = mFallbackLineSpacing
+                    final int ascent = isFallbackLineSpacing
                             ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
                             : fmAscent;
-                    final int descent = mFallbackLineSpacing
+                    final int descent = isFallbackLineSpacing
                             ? Math.max(fmDescent, Math.round(descents[breakIndex]))
                             : fmDescent;
 
                     // The fallback ascent/descent may be larger than top/bottom of the default font
                     // metrics. Adjust top/bottom with ascent/descent for avoiding unexpected
                     // clipping.
-                    if (mFallbackLineSpacing) {
+                    if (isFallbackLineSpacing) {
                         if (ascent < fmTop) {
                             fmTop = ascent;
                         }
@@ -1410,16 +1363,6 @@
         return mLines[mColumns * line + ELLIPSIS_START];
     }
 
-    @Override
-    public int getEllipsizedWidth() {
-        return mEllipsizedWidth;
-    }
-
-    @Override
-    public boolean isFallbackLineSpacingEnabled() {
-        return mFallbackLineSpacing;
-    }
-
     /**
      * Return the total height of this layout.
      *
@@ -1445,8 +1388,6 @@
     private int mTopPadding, mBottomPadding;
     @UnsupportedAppUsage
     private int mColumns;
-    private int mEllipsizedWidth;
-    private boolean mFallbackLineSpacing;
 
     /**
      * Keeps track if ellipsize is applied to the text.
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 4464d19..d31f823 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.input.InputManagerGlobal;
+import android.os.IInputConstants;
 import android.util.ArrayMap;
 import android.util.Pools.SynchronizedPool;
 
@@ -53,11 +54,13 @@
     public @interface VelocityTrackableMotionEventAxis {}
 
     /**
-     * Velocity Tracker Strategy: Invalid.
+     * Use the default Velocity Tracker Strategy. Different axes may use different default
+     * strategies.
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT = -1;
+    public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_DEFAULT;
 
     /**
      * Velocity Tracker Strategy: Impulse.
@@ -66,7 +69,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE = 0;
+    public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_IMPULSE;
 
     /**
      * Velocity Tracker Strategy: LSQ1.
@@ -77,7 +81,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 = 1;
+    public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ1;
 
     /**
      * Velocity Tracker Strategy: LSQ2.
@@ -88,7 +93,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 = 2;
+    public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ2;
 
     /**
      * Velocity Tracker Strategy: LSQ3.
@@ -98,7 +104,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 = 3;
+    public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ3;
 
     /**
      * Velocity Tracker Strategy: WLSQ2_DELTA.
@@ -106,7 +113,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 4;
+    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA;
 
     /**
      * Velocity Tracker Strategy: WLSQ2_CENTRAL.
@@ -114,7 +122,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 5;
+    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL;
 
     /**
      * Velocity Tracker Strategy: WLSQ2_RECENT.
@@ -122,7 +131,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 6;
+    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT;
 
     /**
      * Velocity Tracker Strategy: INT1.
@@ -134,7 +144,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_INT1 = 7;
+    public static final int VELOCITY_TRACKER_STRATEGY_INT1 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_INT1;
 
     /**
      * Velocity Tracker Strategy: INT2.
@@ -144,7 +155,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_INT2 = 8;
+    public static final int VELOCITY_TRACKER_STRATEGY_INT2 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_INT2;
 
     /**
      * Velocity Tracker Strategy: Legacy.
@@ -155,7 +167,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_LEGACY = 9;
+    public static final int VELOCITY_TRACKER_STRATEGY_LEGACY =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_LEGACY;
 
 
     /**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5d1a81f..bb1d5b1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1302,7 +1302,7 @@
      * ratio or orientation specified in the app manifest.
      *
      * <p>The aspect ratio compatibility override is exposed to users in device
-     * settings. A menu in device settings lists all apps that don't opt out of
+     * settings. A menu in device settings lists all apps that have not opted out of
      * the compatibility override. Users select apps from the menu and set the
      * app aspect ratio on a per-app basis. Typically, the menu is available
      * only on large screen devices.
@@ -1347,11 +1347,11 @@
      * Application level
      * {@link android.content.pm.PackageManager.Property PackageManager.Property}
      * tag that (when set to false) informs the system the app has opted out of the
-     * full-screen option of the aspect ratio compatibility override. (For
-     * background information about the aspect ratio compatibility override, see
+     * full-screen option of the user aspect ratio compatibility override settings. (For
+     * background information about the user aspect ratio compatibility override, see
      * {@link #PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE}.)
      *
-     * <p>When users apply the aspect ratio compatibility override, the orientation
+     * <p>When users apply the full-screen compatibility override, the orientation
      * of the activity is forced to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER}.
      *
      * <p>The user override is intended to improve the app experience on devices
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 0a938ef4..79152b4 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -185,7 +185,7 @@
     private boolean mHaveBatteryLevel;
     private boolean mRecordingHistory;
 
-    private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
+    static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
     private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024;
 
     private final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
@@ -1872,6 +1872,7 @@
             }
             return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
         } else {
+            tag.poolIdx = HistoryTag.HISTORY_TAG_POOL_OVERFLOW;
             // Tag pool overflow: include the tag itself in the parcel
             return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
         }
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index ccc3454..4c2b2854 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -309,7 +309,11 @@
             BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag();
             tag.readFromParcel(src);
             tag.poolIdx = index & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
-            mHistoryTags.put(tag.poolIdx, tag);
+            if (tag.poolIdx < BatteryStatsHistory.HISTORY_TAG_INDEX_LIMIT) {
+                mHistoryTags.put(tag.poolIdx, tag);
+            } else {
+                tag.poolIdx = BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW;
+            }
 
             outTag.setTo(tag);
         } else {
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index 05c9f68..03e9a6a 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -16,13 +16,14 @@
 
 #define LOG_TAG "VelocityTracker-JNI"
 
+#include <android-base/logging.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <cutils/properties.h>
 #include <input/Input.h>
 #include <input/VelocityTracker.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
-#include <utils/Log.h>
+
 #include "android_view_MotionEvent.h"
 #include "core_jni_helpers.h"
 
@@ -102,7 +103,7 @@
         jobject eventObj) {
     const MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
     if (!event) {
-        ALOGW("nativeAddMovement failed because MotionEvent was finalized.");
+        LOG(WARNING) << "nativeAddMovement failed because MotionEvent was finalized.";
         return;
     }
 
diff --git a/core/res/res/raw/default_ringtone_vibration_effect.ahv b/core/res/res/raw/default_ringtone_vibration_effect.ahv
new file mode 100644
index 0000000..c66fc04
--- /dev/null
+++ b/core/res/res/raw/default_ringtone_vibration_effect.ahv
@@ -0,0 +1,47 @@
+<?xml version='1.0' encoding='utf-8' standalone='no' ?>
+
+<!--
+**
+** 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.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<vibration>
+    <waveform-effect>
+        <waveform-entry amplitude="0" durationMs="0" />
+        <waveform-entry amplitude="255" durationMs="12" />
+        <waveform-entry amplitude="0" durationMs="250" />
+        <waveform-entry amplitude="255" durationMs="12" />
+        <waveform-entry amplitude="0" durationMs="500" />
+        <repeating>
+            <waveform-entry amplitude="77" durationMs="50" />
+            <waveform-entry amplitude="77" durationMs="50" />
+            <waveform-entry amplitude="78" durationMs="50" />
+            <waveform-entry amplitude="79" durationMs="50" />
+            <waveform-entry amplitude="81" durationMs="50" />
+            <waveform-entry amplitude="84" durationMs="50" />
+            <waveform-entry amplitude="87" durationMs="50" />
+            <waveform-entry amplitude="93" durationMs="50" />
+            <waveform-entry amplitude="101" durationMs="50" />
+            <waveform-entry amplitude="114" durationMs="50" />
+            <waveform-entry amplitude="133" durationMs="50" />
+            <waveform-entry amplitude="162" durationMs="50" />
+            <waveform-entry amplitude="205" durationMs="50" />
+            <waveform-entry amplitude="255" durationMs="50" />
+            <waveform-entry amplitude="255" durationMs="300" />
+            <waveform-entry amplitude="0" durationMs="1000" />
+        </repeating>
+    </waveform-effect>
+</vibration>
\ No newline at end of file
diff --git a/core/res/res/values/config_battery_saver.xml b/core/res/res/values/config_battery_saver.xml
index eb396df..e1b0ef4 100644
--- a/core/res/res/values/config_battery_saver.xml
+++ b/core/res/res/values/config_battery_saver.xml
@@ -26,6 +26,10 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
 
+    <!-- Whether or not the device supports battery saver. If false, battery saver will be
+     disabled. -->
+    <bool name="config_batterySaverSupported">true</bool>
+
     <!-- Whether or not battery saver should be "sticky" when manually enabled. -->
     <bool name="config_batterySaverStickyBehaviourDisabled">false</bool>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0a0dc36..851c0c2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4019,6 +4019,7 @@
 
   <!-- Battery saver config -->
   <java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
+  <java-symbol type="bool" name="config_batterySaverSupported" />
   <java-symbol type="string" name="config_batterySaverDeviceSpecificConfig" />
   <java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
   <java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
@@ -5193,4 +5194,6 @@
 
   <!-- For ActivityManager PSS profiling configurability -->
   <java-symbol type="bool" name="config_am_disablePssProfiling" />
+
+  <java-symbol type="raw" name="default_ringtone_vibration_effect" />
 </resources>
diff --git a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
index 2232e3a..e1f9523 100644
--- a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
+++ b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
@@ -20,6 +20,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.ConditionVariable;
 import android.os.PowerMonitor;
 import android.os.PowerMonitorReadings;
 
@@ -29,13 +30,16 @@
 import java.util.List;
 
 public class SystemHealthManagerTest {
+    private List<PowerMonitor> mPowerMonitorInfo;
+    private PowerMonitorReadings mReadings;
+    private RuntimeException mException;
 
     @Test
     public void getPowerMonitors() {
         SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
-        PowerMonitor[] powerMonitorInfo = shm.getSupportedPowerMonitors();
+        List<PowerMonitor> powerMonitorInfo = shm.getSupportedPowerMonitors();
         assertThat(powerMonitorInfo).isNotNull();
-        if (powerMonitorInfo.length == 0) {
+        if (powerMonitorInfo.isEmpty()) {
             // This device does not support PowerStats HAL
             return;
         }
@@ -50,20 +54,73 @@
             }
         }
 
-        List<PowerMonitor> pmis = new ArrayList<>();
+        List<PowerMonitor> selectedMonitors = new ArrayList<>();
         if (consumerMonitor != null) {
-            pmis.add(consumerMonitor);
+            selectedMonitors.add(consumerMonitor);
         }
         if (measurementMonitor != null) {
-            pmis.add(measurementMonitor);
+            selectedMonitors.add(measurementMonitor);
         }
 
-        PowerMonitor[] selectedMonitors = pmis.toArray(new PowerMonitor[0]);
         PowerMonitorReadings readings = shm.getPowerMonitorReadings(selectedMonitors);
 
         for (PowerMonitor monitor : selectedMonitors) {
             assertThat(readings.getConsumedEnergyUws(monitor)).isAtLeast(0);
-            assertThat(readings.getTimestampMs(monitor)).isGreaterThan(0);
+            assertThat(readings.getTimestamp(monitor)).isGreaterThan(0);
+        }
+    }
+
+    @Test
+    public void getPowerMonitorsAsync() {
+        SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
+        ConditionVariable done = new ConditionVariable();
+        shm.getSupportedPowerMonitors(null, pms -> {
+            mPowerMonitorInfo = pms;
+            done.open();
+        });
+        done.block();
+        assertThat(mPowerMonitorInfo).isNotNull();
+        if (mPowerMonitorInfo.isEmpty()) {
+            // This device does not support PowerStats HAL
+            return;
+        }
+
+        PowerMonitor consumerMonitor = null;
+        PowerMonitor measurementMonitor = null;
+        for (PowerMonitor pmi : mPowerMonitorInfo) {
+            if (pmi.type == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) {
+                measurementMonitor = pmi;
+            } else {
+                consumerMonitor = pmi;
+            }
+        }
+
+        List<PowerMonitor> selectedMonitors = new ArrayList<>();
+        if (consumerMonitor != null) {
+            selectedMonitors.add(consumerMonitor);
+        }
+        if (measurementMonitor != null) {
+            selectedMonitors.add(measurementMonitor);
+        }
+
+        done.close();
+        shm.getPowerMonitorReadings(selectedMonitors, null,
+                readings -> {
+                    mReadings = readings;
+                    done.open();
+                },
+                exception -> {
+                    mException = exception;
+                    done.open();
+                }
+        );
+        done.block();
+
+        assertThat(mException).isNull();
+
+        for (PowerMonitor monitor : selectedMonitors) {
+            assertThat(mReadings.getConsumedEnergyUws(monitor)).isAtLeast(0);
+            assertThat(mReadings.getTimestamp(monitor)).isGreaterThan(0);
         }
     }
 }
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 5fe17ee..4d446901 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -36,6 +37,7 @@
 import android.graphics.drawable.Icon;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.service.controls.actions.CommandAction;
@@ -53,6 +55,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -307,6 +310,18 @@
                 intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL)));
     }
 
+    @Test
+    public void testOnNextDoesntRethrowDeadObjectException() throws RemoteException {
+        doAnswer(invocation -> {
+            throw new DeadObjectException();
+        }).when(mSubscriber).onNext(ArgumentMatchers.any(), ArgumentMatchers.any());
+        Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
+
+        sendControlGetControl(control);
+
+        assertTrue(mControlsProviderService.mSubscription.mIsCancelled);
+    }
+
     /**
      * Sends the control through the publisher in {@code mControlsProviderService}, returning
      * the control obtained by the subscriber
@@ -359,6 +374,7 @@
         }
 
         private List<Control> mControls;
+        private FakeSubscription mSubscription;
 
         public void setControls(List<Control> controls) {
             mControls = controls;
@@ -398,17 +414,35 @@
         }
 
         private Subscription createSubscription(Subscriber s, List<Control> controls) {
-            return new Subscription() {
-                public void request(long n) {
-                    int i = 0;
-                    for (Control c : mControls) {
-                        if (i++ < n) s.onNext(c);
-                        else break;
-                    }
-                    s.onComplete();
-                }
-                public void cancel() {}
-            };
+            FakeSubscription subscription = new FakeSubscription(s, controls);
+            mSubscription = subscription;
+            return subscription;
+        }
+    }
+
+    private static final class FakeSubscription implements Subscription {
+
+        private final Subscriber mSubscriber;
+        private final List<Control> mControls;
+
+        private boolean mIsCancelled = false;
+
+        FakeSubscription(Subscriber s, List<Control> controls) {
+            mSubscriber = s;
+            mControls = controls;
+        }
+
+        public void request(long n) {
+            int i = 0;
+            for (Control c : mControls) {
+                if (i++ < n) mSubscriber.onNext(c);
+                else break;
+            }
+            mSubscriber.onComplete();
+        }
+
+        public void cancel() {
+            mIsCancelled = true;
         }
     }
 }
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java
index a310edc..da8e4cc 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorVisibilityTest.java
@@ -20,9 +20,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
+
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Handler;
+import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.UserHandle;
 
@@ -45,6 +48,25 @@
             TEST_DATA_PATH + "TestVisibilityApp.apk";
     private static final String TEAT_APK_PACKAGE_NAME = "com.example.android.testvisibilityapp";
     private static final int WAIT_CALLBACK_CALLED_IN_SECONDS = 1;
+
+    @Test
+    public void testPackageMonitorCallbackMultipleRegisterThrowsException() throws Exception {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        final IRemoteCallback callback = new IRemoteCallback.Stub() {
+            @Override
+            public void sendResult(android.os.Bundle bundle) {
+                // do-nothing
+            }
+        };
+        try {
+            context.getPackageManager().registerPackageMonitorCallback(callback, 0);
+            assertThrows(IllegalStateException.class,
+                    () -> context.getPackageManager().registerPackageMonitorCallback(callback, 0));
+        } finally {
+            context.getPackageManager().unregisterPackageMonitorCallback(callback);
+        }
+    }
+
     @Test
     public void testPackageMonitorPackageVisible() throws Exception {
         TestVisibilityPackageMonitor testPackageMonitor = new TestVisibilityPackageMonitor();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 079cfa3..81384ca 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -242,9 +242,20 @@
                 return false;
             }
 
+            // Abort if no space to split.
+            final SplitAttributes calculatedSplitAttributes = mPresenter.computeSplitAttributes(
+                    task.getTaskProperties(), splitPinRule,
+                    splitPinRule.getDefaultSplitAttributes(),
+                    getActivitiesMinDimensionsPair(primaryContainer.getTopNonFinishingActivity(),
+                            topContainer.getTopNonFinishingActivity()));
+            if (!SplitPresenter.shouldShowSplit(calculatedSplitAttributes)) {
+                Log.w(TAG, "No space to split, abort pinning top ActivityStack.");
+                return false;
+            }
+
             // Registers a Split
             final SplitPinContainer splitPinContainer = new SplitPinContainer(primaryContainer,
-                    topContainer, splitPinRule, splitPinRule.getDefaultSplitAttributes());
+                    topContainer, splitPinRule, calculatedSplitAttributes);
             task.addSplitContainer(splitPinContainer);
 
             // Updates the Split
@@ -263,7 +274,33 @@
 
     @Override
     public void unpinTopActivityStack(int taskId){
-        // TODO
+        synchronized (mLock) {
+            final TaskContainer task = getTaskContainer(taskId);
+            if (task == null) {
+                Log.e(TAG, "Cannot find the task to unpin, id: " + taskId);
+                return;
+            }
+
+            final SplitPinContainer splitPinContainer = task.getSplitPinContainer();
+            if (splitPinContainer == null) {
+                Log.e(TAG, "No ActivityStack is pinned.");
+                return;
+            }
+
+            // Remove the SplitPinContainer from the task.
+            final TaskFragmentContainer containerToUnpin =
+                    splitPinContainer.getSecondaryContainer();
+            task.removeSplitPinContainer();
+
+            // Resets the isolated navigation and updates the container.
+            final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
+            final WindowContainerTransaction wct = transactionRecord.getTransaction();
+            mPresenter.setTaskFragmentIsolatedNavigation(wct,
+                    containerToUnpin.getTaskFragmentToken(), false /* isolated */);
+            updateContainer(wct, containerToUnpin);
+            transactionRecord.apply(false /* shouldApplyIndependently */);
+            updateCallbackIfNecessary();
+        }
     }
 
     @Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 16d8cb4..9a0769a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -265,8 +265,25 @@
     }
 
     void removeSplitPinContainer() {
+        if (mSplitPinContainer == null) {
+            return;
+        }
+
+        final TaskFragmentContainer primaryContainer = mSplitPinContainer.getPrimaryContainer();
+        final TaskFragmentContainer secondaryContainer = mSplitPinContainer.getSecondaryContainer();
         mSplitContainers.remove(mSplitPinContainer);
         mSplitPinContainer = null;
+
+        // Remove the other SplitContainers that contains the unpinned container (unless it
+        // is the current top-most split-pair), since the state are no longer valid.
+        final List<SplitContainer> splitsToRemove = new ArrayList<>();
+        for (SplitContainer splitContainer : mSplitContainers) {
+            if (splitContainer.getSecondaryContainer().equals(secondaryContainer)
+                    && !splitContainer.getPrimaryContainer().equals(primaryContainer)) {
+                splitsToRemove.add(splitContainer);
+            }
+        }
+        removeSplitContainers(splitsToRemove);
     }
 
     @Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 62b0799..0998e71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -342,6 +342,7 @@
                 if (!mActiveLetterboxEduLayout.updateCompatInfo(taskInfo, taskListener,
                         showOnDisplay(mActiveLetterboxEduLayout.getDisplayId()))) {
                     // The layout is no longer eligible to be shown, clear active layout.
+                    mActiveLetterboxEduLayout.release();
                     mActiveLetterboxEduLayout = null;
                 }
                 return;
@@ -371,15 +372,9 @@
             ShellTaskOrganizer.TaskListener taskListener) {
         return new LetterboxEduWindowManager(context, taskInfo,
                 mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
-                mTransitionsLazy.get(), this::onLetterboxEduDismissed, mDockStateReader,
-                mCompatUIConfiguration);
-    }
-
-    private void onLetterboxEduDismissed(
-            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
-        mActiveLetterboxEduLayout = null;
-        // We need to update the UI
-        createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second);
+                mTransitionsLazy.get(),
+                stateInfo -> createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second),
+                mDockStateReader, mCompatUIConfiguration);
     }
 
     private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo,
@@ -448,6 +443,7 @@
                 if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener,
                         showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) {
                     // The layout is no longer eligible to be shown, remove from active layouts.
+                    mActiveReachabilityEduLayout.release();
                     mActiveReachabilityEduLayout = null;
                 }
                 return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index b2a189b..ee55211 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -62,13 +62,16 @@
     private SurfaceControl mLeash;
     private TvPipMenuView mPipMenuView;
     private TvPipBackgroundView mPipBackgroundView;
-    private boolean mMenuIsFocused;
 
     @TvPipMenuMode
     private int mCurrentMenuMode = MODE_NO_MENU;
     @TvPipMenuMode
     private int mPrevMenuMode = MODE_NO_MENU;
 
+    /** When the window gains focus, enter this menu mode */
+    @TvPipMenuMode
+    private int mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU;
+
     @IntDef(prefix = { "MODE_" }, value = {
         MODE_NO_MENU,
         MODE_MOVE_MENU,
@@ -170,6 +173,9 @@
         mPipMenuView = createTvPipMenuView();
         setUpViewSurfaceZOrder(mPipMenuView, 1);
         addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE);
+        mPipMenuView.getViewTreeObserver().addOnWindowFocusChangeListener(hasFocus -> {
+            onPipWindowFocusChanged(hasFocus);
+        });
     }
 
     @VisibleForTesting
@@ -224,13 +230,14 @@
     void showMovementMenu() {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "%s: showMovementMenu()", TAG);
-        switchToMenuMode(MODE_MOVE_MENU);
+        requestMenuMode(MODE_MOVE_MENU);
     }
 
     @Override
     public void showMenu() {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG);
-        switchToMenuMode(MODE_ALL_ACTIONS_MENU, true);
+        mPipMenuView.resetMenu();
+        requestMenuMode(MODE_ALL_ACTIONS_MENU);
     }
 
     void onPipTransitionToTargetBoundsStarted(Rect targetBounds) {
@@ -250,7 +257,7 @@
     void closeMenu() {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "%s: closeMenu()", TAG);
-        switchToMenuMode(MODE_NO_MENU);
+        requestMenuMode(MODE_NO_MENU);
     }
 
     @Override
@@ -392,11 +399,15 @@
         }
     }
 
-    // Start methods handling {@link TvPipMenuMode}
+    // Beginning of convenience methods for {@link TvPipMenuMode}
 
     @VisibleForTesting
     boolean isMenuOpen() {
-        return mCurrentMenuMode != MODE_NO_MENU;
+        return isMenuOpen(mCurrentMenuMode);
+    }
+
+    private static boolean isMenuOpen(@TvPipMenuMode int menuMode) {
+        return menuMode != MODE_NO_MENU;
     }
 
     @VisibleForTesting
@@ -409,46 +420,6 @@
         return mCurrentMenuMode == MODE_ALL_ACTIONS_MENU;
     }
 
-    private void switchToMenuMode(@TvPipMenuMode int menuMode) {
-        switchToMenuMode(menuMode, false);
-    }
-
-    private void switchToMenuMode(@TvPipMenuMode int menuMode, boolean resetMenu) {
-        // Note: we intentionally don't return early here, because the TvPipMenuView needs to
-        // refresh the Ui even if there is no menu mode change.
-        mPrevMenuMode = mCurrentMenuMode;
-        mCurrentMenuMode = menuMode;
-
-        ProtoLog.i(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "%s: switchToMenuMode: setting mCurrentMenuMode=%s, mPrevMenuMode=%s", TAG,
-                getMenuModeString(), getMenuModeString(mPrevMenuMode));
-
-        updateUiOnNewMenuModeRequest(resetMenu);
-        updateDelegateOnNewMenuModeRequest();
-    }
-
-    private void updateUiOnNewMenuModeRequest(boolean resetMenu) {
-        if (mPipMenuView == null || mPipBackgroundView == null) return;
-
-        mPipMenuView.setPipGravity(mTvPipBoundsState.getTvPipGravity());
-        mPipMenuView.transitionToMenuMode(mCurrentMenuMode, resetMenu);
-        mPipBackgroundView.transitionToMenuMode(mCurrentMenuMode);
-        grantPipMenuFocus(mCurrentMenuMode != MODE_NO_MENU);
-    }
-
-    private void updateDelegateOnNewMenuModeRequest() {
-        if (mPrevMenuMode == mCurrentMenuMode) return;
-        if (mDelegate == null) return;
-
-        if (mPrevMenuMode == MODE_MOVE_MENU || isInMoveMode()) {
-            mDelegate.onInMoveModeChanged();
-        }
-
-        if (mCurrentMenuMode == MODE_NO_MENU) {
-            mDelegate.onMenuClosed();
-        }
-    }
-
     @VisibleForTesting
     String getMenuModeString() {
         return getMenuModeString(mCurrentMenuMode);
@@ -467,6 +438,90 @@
         }
     }
 
+    // Beginning of methods handling switching between menu modes
+
+    private void requestMenuMode(@TvPipMenuMode int menuMode) {
+        if (isMenuOpen() == isMenuOpen(menuMode)) {
+            // No need to request a focus change. We can directly switch to the new mode.
+            switchToMenuMode(menuMode);
+        } else {
+            if (isMenuOpen(menuMode)) {
+                mMenuModeOnFocus = menuMode;
+            }
+
+            // Send a request to gain window focus if the menu is open, or lose window focus
+            // otherwise. Once the focus change happens, we will request the new mode in the
+            // callback {@link #onPipWindowFocusChanged}.
+            requestPipMenuFocus(isMenuOpen(menuMode));
+        }
+        // Note: we don't handle cases where there is a focus change currently in flight, because
+        // this is very unlikely to happen in practice and would complicate the logic.
+    }
+
+    private void requestPipMenuFocus(boolean focus) {
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: requestPipMenuFocus(%b)", TAG, focus);
+
+        try {
+            WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+                    mSystemWindows.getFocusGrantToken(mPipMenuView), focus);
+        } catch (Exception e) {
+            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                    "%s: Unable to update focus, %s", TAG, e);
+        }
+    }
+
+    /**
+     * Called when the menu window gains or loses focus.
+     */
+    @VisibleForTesting
+    void onPipWindowFocusChanged(boolean focused) {
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: onPipWindowFocusChanged - focused=%b", TAG, focused);
+        switchToMenuMode(focused ? mMenuModeOnFocus : MODE_NO_MENU);
+
+        // Reset the default menu mode for focused state.
+        mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU;
+    }
+
+    /**
+     * Immediately switches to the menu mode in the given request. Updates the mDelegate and the UI.
+     * Doesn't handle any focus changes.
+     */
+    private void switchToMenuMode(@TvPipMenuMode int menuMode) {
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: switchToMenuMode: from=%s, to=%s", TAG, getMenuModeString(),
+                getMenuModeString(menuMode));
+
+        if (mCurrentMenuMode == menuMode) return;
+
+        mPrevMenuMode = mCurrentMenuMode;
+        mCurrentMenuMode = menuMode;
+        updateUiOnNewMenuModeRequest();
+        updateDelegateOnNewMenuModeRequest();
+    }
+
+    private void updateUiOnNewMenuModeRequest() {
+        if (mPipMenuView == null || mPipBackgroundView == null) return;
+
+        mPipMenuView.setPipGravity(mTvPipBoundsState.getTvPipGravity());
+        mPipMenuView.transitionToMenuMode(mCurrentMenuMode);
+        mPipBackgroundView.transitionToMenuMode(mCurrentMenuMode);
+    }
+
+    private void updateDelegateOnNewMenuModeRequest() {
+        if (mPrevMenuMode == mCurrentMenuMode) return;
+        if (mDelegate == null) return;
+
+        if (mPrevMenuMode == MODE_MOVE_MENU || isInMoveMode()) {
+            mDelegate.onInMoveModeChanged();
+        }
+
+        if (!isMenuOpen()) {
+            mDelegate.onMenuClosed();
+        }
+    }
+
     // Start {@link TvPipMenuView.Delegate} methods
 
     @Override
@@ -476,42 +531,19 @@
     }
 
     @Override
-    public void onBackPress() {
-        if (!onExitMoveMode()) {
-            closeMenu();
-        }
-    }
-
-    @Override
-    public boolean onExitMoveMode() {
+    public void onExitCurrentMenuMode() {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "%s: onExitMoveMode - mCurrentMenuMode=%s", TAG, getMenuModeString());
-
-        final int saveMenuMode = mCurrentMenuMode;
-        if (isInMoveMode()) {
-            switchToMenuMode(mPrevMenuMode);
-        }
-        return saveMenuMode == MODE_MOVE_MENU;
+                "%s: onExitCurrentMenuMode - mCurrentMenuMode=%s", TAG, getMenuModeString());
+        requestMenuMode(isInMoveMode() ? mPrevMenuMode : MODE_NO_MENU);
     }
 
     @Override
-    public boolean onPipMovement(int keycode) {
+    public void onPipMovement(int keycode) {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "%s: onPipMovement - mCurrentMenuMode=%s", TAG, getMenuModeString());
         if (isInMoveMode()) {
             mDelegate.movePip(keycode);
         }
-        return isInMoveMode();
-    }
-
-    @Override
-    public void onPipWindowFocusChanged(boolean focused) {
-        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "%s: onPipWindowFocusChanged - focused=%b", TAG, focused);
-        mMenuIsFocused = focused;
-        if (!focused && isMenuOpen()) {
-            closeMenu();
-        }
     }
 
     interface Delegate {
@@ -524,21 +556,6 @@
         void closeEduText();
     }
 
-    private void grantPipMenuFocus(boolean grantFocus) {
-        if (mMenuIsFocused == grantFocus) return;
-
-        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "%s: grantWindowFocus(%b)", TAG, grantFocus);
-
-        try {
-            WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
-                    mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus);
-        } catch (Exception e) {
-            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                    "%s: Unable to update focus, %s", TAG, e);
-        }
-    }
-
     private class PipMenuSurfaceChangedCallback implements ViewRootImpl.SurfaceChangedCallback {
         private final View mView;
         private final int mZOrder;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 613791c..7c15637 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -328,7 +328,7 @@
         return menuUiBounds;
     }
 
-    void transitionToMenuMode(int menuMode, boolean resetMenu) {
+    void transitionToMenuMode(int menuMode) {
         switch (menuMode) {
             case MODE_NO_MENU:
                 hideAllUserControls();
@@ -337,7 +337,7 @@
                 showMoveMenu();
                 break;
             case MODE_ALL_ACTIONS_MENU:
-                showAllActionsMenu(resetMenu);
+                showAllActionsMenu();
                 break;
             default:
                 throw new IllegalArgumentException(
@@ -362,13 +362,13 @@
         mEduTextDrawer.closeIfNeeded();
     }
 
-    private void showAllActionsMenu(boolean resetMenu) {
-        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "%s: showAllActionsMenu(), resetMenu %b", TAG, resetMenu);
+    void resetMenu() {
+        scrollToFirstAction();
+    }
 
-        if (resetMenu) {
-            scrollToFirstAction();
-        }
+    private void showAllActionsMenu() {
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: showAllActionsMenu()", TAG);
 
         if (mCurrentMenuMode == MODE_ALL_ACTIONS_MENU) return;
 
@@ -431,12 +431,6 @@
         }
     }
 
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        super.onWindowFocusChanged(hasWindowFocus);
-        mListener.onPipWindowFocusChanged(hasWindowFocus);
-    }
-
     private void animateAlphaTo(float alpha, View view) {
         if (view.getAlpha() == alpha) {
             return;
@@ -483,28 +477,28 @@
         if (event.getAction() == ACTION_UP) {
 
             if (event.getKeyCode() == KEYCODE_BACK) {
-                mListener.onBackPress();
+                mListener.onExitCurrentMenuMode();
                 return true;
             }
 
-            if (mA11yManager.isEnabled()) {
-                return super.dispatchKeyEvent(event);
-            }
-
-            switch (event.getKeyCode()) {
-                case KEYCODE_DPAD_UP:
-                case KEYCODE_DPAD_DOWN:
-                case KEYCODE_DPAD_LEFT:
-                case KEYCODE_DPAD_RIGHT:
-                    return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent(
-                            event);
-                case KEYCODE_ENTER:
-                case KEYCODE_DPAD_CENTER:
-                    return mListener.onExitMoveMode() || super.dispatchKeyEvent(event);
-                default:
-                    break;
+            if (mCurrentMenuMode == MODE_MOVE_MENU && !mA11yManager.isEnabled()) {
+                switch (event.getKeyCode()) {
+                    case KEYCODE_DPAD_UP:
+                    case KEYCODE_DPAD_DOWN:
+                    case KEYCODE_DPAD_LEFT:
+                    case KEYCODE_DPAD_RIGHT:
+                        mListener.onPipMovement(event.getKeyCode());
+                        return true;
+                    case KEYCODE_ENTER:
+                    case KEYCODE_DPAD_CENTER:
+                        mListener.onExitCurrentMenuMode();
+                        return true;
+                    default:
+                        // Dispatch key event as normal below
+                }
             }
         }
+
         return super.dispatchKeyEvent(event);
     }
 
@@ -529,7 +523,7 @@
         if (a11yEnabled) {
             mA11yDoneButton.setVisibility(VISIBLE);
             mA11yDoneButton.setOnClickListener(v -> {
-                mListener.onExitMoveMode();
+                mListener.onExitCurrentMenuMode();
             });
             mA11yDoneButton.requestFocus();
             mA11yDoneButton.requestAccessibilityFocus();
@@ -626,26 +620,15 @@
 
     interface Listener {
 
-        void onBackPress();
+        /**
+         * Called when a button for exiting the current menu mode was pressed.
+         */
+        void onExitCurrentMenuMode();
 
         /**
-         * Called when a button for exiting move mode was pressed.
-         *
-         * @return true if the event was handled or false if the key event should be handled by the
-         * next receiver.
+         * Called when a button to move the PiP in a certain direction, indicated by keycode.
          */
-        boolean onExitMoveMode();
-
-        /**
-         * @return whether pip movement was handled.
-         */
-        boolean onPipMovement(int keycode);
-
-        /**
-         * Called when the TvPipMenuView loses focus. This also means that the TV PiP menu window
-         * has lost focus.
-         */
-        void onPipWindowFocusChanged(boolean focused);
+        void onPipMovement(int keycode);
 
         /**
          *  The edu text closing impacts the size of the Picture-in-Picture window and influences
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
index 3a08d32..e26dc7c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java
@@ -25,18 +25,24 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.os.Handler;
+import android.os.Looper;
 import android.view.SurfaceControl;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnWindowFocusChangeListener;
 
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.SystemWindows;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -50,28 +56,38 @@
     @Mock
     private SystemWindows mMockSystemWindows;
     @Mock
-    private SurfaceControl mMockPipLeash;
-    @Mock
-    private Handler mMockHandler;
-    @Mock
-    private TvPipActionsProvider mMockActionsProvider;
-    @Mock
     private TvPipMenuView mMockTvPipMenuView;
     @Mock
     private TvPipBackgroundView mMockTvPipBackgroundView;
 
+    private Handler mMainHandler;
     private TvPipMenuController mTvPipMenuController;
+    private OnWindowFocusChangeListener mFocusChangeListener;
 
     @Before
     public void setUp() {
         assumeTrue(isTelevision());
 
         MockitoAnnotations.initMocks(this);
+        mMainHandler = new Handler(Looper.getMainLooper());
+
+        final ViewTreeObserver mockMenuTreeObserver = mock(ViewTreeObserver.class);
+        doReturn(mockMenuTreeObserver).when(mMockTvPipMenuView).getViewTreeObserver();
 
         mTvPipMenuController = new TestTvPipMenuController();
         mTvPipMenuController.setDelegate(mMockDelegate);
-        mTvPipMenuController.setTvPipActionsProvider(mMockActionsProvider);
-        mTvPipMenuController.attach(mMockPipLeash);
+        mTvPipMenuController.setTvPipActionsProvider(mock(TvPipActionsProvider.class));
+        mTvPipMenuController.attach(mock(SurfaceControl.class));
+        mFocusChangeListener = captureFocusChangeListener(mockMenuTreeObserver);
+    }
+
+    private OnWindowFocusChangeListener captureFocusChangeListener(
+            ViewTreeObserver mockTreeObserver) {
+        final ArgumentCaptor<OnWindowFocusChangeListener> focusChangeListenerCaptor =
+                ArgumentCaptor.forClass(OnWindowFocusChangeListener.class);
+        verify(mockTreeObserver).addOnWindowFocusChangeListener(
+                focusChangeListenerCaptor.capture());
+        return focusChangeListenerCaptor.getValue();
     }
 
     @Test
@@ -81,24 +97,25 @@
 
     @Test
     public void testSwitch_FromNoMenuMode_ToMoveMode() {
-        showAndAssertMoveMenu();
+        showAndAssertMoveMenu(true);
     }
 
     @Test
     public void testSwitch_FromNoMenuMode_ToAllActionsMode() {
-        showAndAssertAllActionsMenu();
+        showAndAssertAllActionsMenu(true);
     }
 
     @Test
     public void testSwitch_FromMoveMode_ToAllActionsMode() {
-        showAndAssertMoveMenu();
-        showAndAssertAllActionsMenu();
+        showAndAssertMoveMenu(true);
+        showAndAssertAllActionsMenu(false);
+        verify(mMockDelegate, times(2)).onInMoveModeChanged();
     }
 
     @Test
     public void testSwitch_FromAllActionsMode_ToMoveMode() {
-        showAndAssertAllActionsMenu();
-        showAndAssertMoveMenu();
+        showAndAssertAllActionsMenu(true);
+        showAndAssertMoveMenu(false);
     }
 
     @Test
@@ -110,187 +127,282 @@
 
     @Test
     public void testCloseMenu_MoveMode() {
-        showAndAssertMoveMenu();
+        showAndAssertMoveMenu(true);
 
-        closeMenuAndAssertMenuClosed();
+        closeMenuAndAssertMenuClosed(true);
         verify(mMockDelegate, times(2)).onInMoveModeChanged();
     }
 
     @Test
     public void testCloseMenu_AllActionsMode() {
-        showAndAssertAllActionsMenu();
+        showAndAssertAllActionsMenu(true);
 
-        closeMenuAndAssertMenuClosed();
+        closeMenuAndAssertMenuClosed(true);
+    }
+
+    @Test
+    public void testCloseMenu_MoveModeFollowedByMoveMode() {
+        showAndAssertMoveMenu(true);
+        showAndAssertMoveMenu(false);
+
+        closeMenuAndAssertMenuClosed(true);
+        verify(mMockDelegate, times(2)).onInMoveModeChanged();
     }
 
     @Test
     public void testCloseMenu_MoveModeFollowedByAllActionsMode() {
-        showAndAssertMoveMenu();
-        showAndAssertAllActionsMenu();
+        showAndAssertMoveMenu(true);
+        showAndAssertAllActionsMenu(false);
         verify(mMockDelegate, times(2)).onInMoveModeChanged();
 
-        closeMenuAndAssertMenuClosed();
+        closeMenuAndAssertMenuClosed(true);
     }
 
     @Test
     public void testCloseMenu_AllActionsModeFollowedByMoveMode() {
-        showAndAssertAllActionsMenu();
-        showAndAssertMoveMenu();
+        showAndAssertAllActionsMenu(true);
+        showAndAssertMoveMenu(false);
 
-        closeMenuAndAssertMenuClosed();
+        closeMenuAndAssertMenuClosed(true);
         verify(mMockDelegate, times(2)).onInMoveModeChanged();
     }
 
     @Test
-    public void testExitMoveMode_NoMenuMode() {
-        mTvPipMenuController.onExitMoveMode();
-        assertMenuIsOpen(false);
-        verify(mMockDelegate, never()).onMenuClosed();
+    public void testCloseMenu_AllActionsModeFollowedByAllActionsMode() {
+        showAndAssertAllActionsMenu(true);
+        showAndAssertAllActionsMenu(false);
+
+        closeMenuAndAssertMenuClosed(true);
+        verify(mMockDelegate, never()).onInMoveModeChanged();
     }
 
     @Test
-    public void testExitMoveMode_MoveMode() {
-        showAndAssertMoveMenu();
+    public void testExitMenuMode_NoMenuMode() {
+        mTvPipMenuController.onExitCurrentMenuMode();
+        assertMenuIsOpen(false);
+        verify(mMockDelegate, never()).onMenuClosed();
+        verify(mMockDelegate, never()).onInMoveModeChanged();
+    }
 
-        mTvPipMenuController.onExitMoveMode();
+    @Test
+    public void testExitMenuMode_MoveMode() {
+        showAndAssertMoveMenu(true);
+
+        mTvPipMenuController.onExitCurrentMenuMode();
+        mFocusChangeListener.onWindowFocusChanged(false);
         assertMenuClosed();
         verify(mMockDelegate, times(2)).onInMoveModeChanged();
     }
 
     @Test
-    public void testExitMoveMode_AllActionsMode() {
-        showAndAssertAllActionsMenu();
+    public void testExitMenuMode_AllActionsMode() {
+        showAndAssertAllActionsMenu(true);
 
-        mTvPipMenuController.onExitMoveMode();
-        assertMenuIsInAllActionsMode();
-
+        mTvPipMenuController.onExitCurrentMenuMode();
+        mFocusChangeListener.onWindowFocusChanged(false);
+        assertMenuClosed();
     }
 
     @Test
-    public void testExitMoveMode_AllActionsModeFollowedByMoveMode() {
-        showAndAssertAllActionsMenu();
-        showAndAssertMoveMenu();
+    public void testExitMenuMode_AllActionsModeFollowedByMoveMode() {
+        showAndAssertAllActionsMenu(true);
+        showAndAssertMoveMenu(false);
 
-        mTvPipMenuController.onExitMoveMode();
-        assertMenuIsInAllActionsMode();
-        verify(mMockDelegate, times(2)).onInMoveModeChanged();
-        verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false));
-        verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
-    }
-
-    @Test
-    public void testOnBackPress_NoMenuMode() {
-        mTvPipMenuController.onBackPress();
-        assertMenuIsOpen(false);
-        verify(mMockDelegate, never()).onMenuClosed();
-    }
-
-    @Test
-    public void testOnBackPress_MoveMode() {
-        showAndAssertMoveMenu();
-
-        pressBackAndAssertMenuClosed();
-        verify(mMockDelegate, times(2)).onInMoveModeChanged();
-    }
-
-    @Test
-    public void testOnBackPress_AllActionsMode() {
-        showAndAssertAllActionsMenu();
-
-        pressBackAndAssertMenuClosed();
-    }
-
-    @Test
-    public void testOnBackPress_MoveModeFollowedByAllActionsMode() {
-        showAndAssertMoveMenu();
-        showAndAssertAllActionsMenu();
+        mTvPipMenuController.onExitCurrentMenuMode();
+        assertSwitchedToAllActionsMode(2);
         verify(mMockDelegate, times(2)).onInMoveModeChanged();
 
-        pressBackAndAssertMenuClosed();
+        mTvPipMenuController.onExitCurrentMenuMode();
+        mFocusChangeListener.onWindowFocusChanged(false);
+        assertMenuClosed();
     }
 
     @Test
-    public void testOnBackPress_AllActionsModeFollowedByMoveMode() {
-        showAndAssertAllActionsMenu();
-        showAndAssertMoveMenu();
+    public void testExitMenuMode_AllActionsModeFollowedByAllActionsMode() {
+        showAndAssertAllActionsMenu(true);
+        showAndAssertAllActionsMenu(false);
 
-        mTvPipMenuController.onBackPress();
-        assertMenuIsInAllActionsMode();
+        mTvPipMenuController.onExitCurrentMenuMode();
+        mFocusChangeListener.onWindowFocusChanged(false);
+        assertMenuClosed();
+        verify(mMockDelegate, never()).onInMoveModeChanged();
+    }
+
+    @Test
+    public void testExitMenuMode_MoveModeFollowedByAllActionsMode() {
+        showAndAssertMoveMenu(true);
+
+        showAndAssertAllActionsMenu(false);
         verify(mMockDelegate, times(2)).onInMoveModeChanged();
-        verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false));
-        verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
 
-        pressBackAndAssertMenuClosed();
+        mTvPipMenuController.onExitCurrentMenuMode();
+        mFocusChangeListener.onWindowFocusChanged(false);
+        assertMenuClosed();
+    }
+
+    @Test
+    public void testExitMenuMode_MoveModeFollowedByMoveMode() {
+        showAndAssertMoveMenu(true);
+        showAndAssertMoveMenu(false);
+
+        mTvPipMenuController.onExitCurrentMenuMode();
+        mFocusChangeListener.onWindowFocusChanged(false);
+        assertMenuClosed();
+        verify(mMockDelegate, times(2)).onInMoveModeChanged();
     }
 
     @Test
     public void testOnPipMovement_NoMenuMode() {
-        assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
+        moveAndAssertMoveSuccessful(false);
     }
 
     @Test
     public void testOnPipMovement_MoveMode() {
-        showAndAssertMoveMenu();
-        assertPipMoveSuccessful(true, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
-        verify(mMockDelegate).movePip(eq(TEST_MOVE_KEYCODE));
+        showAndAssertMoveMenu(true);
+        moveAndAssertMoveSuccessful(true);
     }
 
     @Test
     public void testOnPipMovement_AllActionsMode() {
-        showAndAssertAllActionsMenu();
-        assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE));
+        showAndAssertAllActionsMenu(true);
+        moveAndAssertMoveSuccessful(false);
     }
 
     @Test
-    public void testOnPipWindowFocusChanged_NoMenuMode() {
-        mTvPipMenuController.onPipWindowFocusChanged(false);
-        assertMenuIsOpen(false);
-    }
+    public void testUnexpectedFocusChanges() {
+        mFocusChangeListener.onWindowFocusChanged(true);
+        assertSwitchedToAllActionsMode(1);
 
-    @Test
-    public void testOnPipWindowFocusChanged_MoveMode() {
-        showAndAssertMoveMenu();
-        mTvPipMenuController.onPipWindowFocusChanged(false);
+        mFocusChangeListener.onWindowFocusChanged(false);
         assertMenuClosed();
+
+        showAndAssertMoveMenu(true);
+        mFocusChangeListener.onWindowFocusChanged(false);
+        assertMenuClosed(2);
+        verify(mMockDelegate, times(2)).onInMoveModeChanged();
     }
 
     @Test
-    public void testOnPipWindowFocusChanged_AllActionsMode() {
-        showAndAssertAllActionsMenu();
-        mTvPipMenuController.onPipWindowFocusChanged(false);
-        assertMenuClosed();
-    }
-
-    private void showAndAssertMoveMenu() {
-        mTvPipMenuController.showMovementMenu();
-        assertMenuIsInMoveMode();
-        verify(mMockDelegate).onInMoveModeChanged();
-        verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_MOVE_MENU), eq(false));
-        verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_MOVE_MENU));
-    }
-
-    private void showAndAssertAllActionsMenu() {
+    public void testAsyncScenario_AllActionsModeRequestFollowedByAsyncMoveModeRequest() {
         mTvPipMenuController.showMenu();
-        assertMenuIsInAllActionsMode();
-        verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(true));
-        verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
+        // Artificially delaying the focus change update and adding a move request to simulate an
+        // async problematic situation.
+        mTvPipMenuController.showMovementMenu();
+        // The first focus change update arrives
+        mFocusChangeListener.onWindowFocusChanged(true);
+
+        // We expect that the TvPipMenuController will directly switch to the "pending" menu mode
+        // - MODE_MOVE_MENU, because no change of focus is needed.
+        assertSwitchedToMoveMode();
     }
 
-    private void closeMenuAndAssertMenuClosed() {
+    @Test
+    public void testAsyncScenario_MoveModeRequestFollowedByAsyncAllActionsModeRequest() {
+        mTvPipMenuController.showMovementMenu();
+        mTvPipMenuController.showMenu();
+
+        mFocusChangeListener.onWindowFocusChanged(true);
+        assertSwitchedToAllActionsMode(1);
+        verify(mMockDelegate, never()).onInMoveModeChanged();
+    }
+
+    @Test
+    public void testAsyncScenario_DropObsoleteIntermediateModeSwitchRequests() {
+        mTvPipMenuController.showMovementMenu();
         mTvPipMenuController.closeMenu();
+
+        // Focus change from showMovementMenu() call.
+        mFocusChangeListener.onWindowFocusChanged(true);
+        assertSwitchedToMoveMode();
+        verify(mMockDelegate).onInMoveModeChanged();
+
+        // Focus change from closeMenu() call.
+        mFocusChangeListener.onWindowFocusChanged(false);
+        assertMenuClosed();
+        verify(mMockDelegate, times(2)).onInMoveModeChanged();
+
+        // Unexpected focus gain should open MODE_ALL_ACTIONS_MENU.
+        mFocusChangeListener.onWindowFocusChanged(true);
+        assertSwitchedToAllActionsMode(1);
+
+        mTvPipMenuController.closeMenu();
+        mTvPipMenuController.showMovementMenu();
+
+        assertSwitchedToMoveMode(2);
+
+        mFocusChangeListener.onWindowFocusChanged(false);
+        assertMenuClosed(2);
+
+        // Closing the menu resets the default menu mode, so the next focus gain opens the menu in
+        // the default mode - MODE_ALL_ACTIONS_MENU.
+        mFocusChangeListener.onWindowFocusChanged(true);
+        assertSwitchedToAllActionsMode(2);
+        verify(mMockDelegate, times(4)).onInMoveModeChanged();
+
+    }
+
+    private void showAndAssertMoveMenu(boolean focusChange) {
+        mTvPipMenuController.showMovementMenu();
+        if (focusChange) {
+            mFocusChangeListener.onWindowFocusChanged(true);
+        }
+        assertSwitchedToMoveMode();
+    }
+
+    private void assertSwitchedToMoveMode() {
+        assertSwitchedToMoveMode(1);
+    }
+
+    private void assertSwitchedToMoveMode(int times) {
+        assertMenuIsInMoveMode();
+        verify(mMockDelegate, times(2 * times - 1)).onInMoveModeChanged();
+        verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU));
+        verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU));
+    }
+
+    private void showAndAssertAllActionsMenu(boolean focusChange) {
+        showAndAssertAllActionsMenu(focusChange, 1);
+    }
+
+    private void showAndAssertAllActionsMenu(boolean focusChange, int times) {
+        mTvPipMenuController.showMenu();
+        if (focusChange) {
+            mFocusChangeListener.onWindowFocusChanged(true);
+        }
+
+        assertSwitchedToAllActionsMode(times);
+    }
+
+    private void assertSwitchedToAllActionsMode(int times) {
+        assertMenuIsInAllActionsMode();
+        verify(mMockTvPipMenuView, times(times))
+                .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
+        verify(mMockTvPipBackgroundView, times(times))
+                .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU));
+    }
+
+    private void closeMenuAndAssertMenuClosed(boolean focusChange) {
+        mTvPipMenuController.closeMenu();
+        if (focusChange) {
+            mFocusChangeListener.onWindowFocusChanged(false);
+        }
         assertMenuClosed();
     }
 
-    private void pressBackAndAssertMenuClosed() {
-        mTvPipMenuController.onBackPress();
-        assertMenuClosed();
+    private void moveAndAssertMoveSuccessful(boolean expectedSuccess) {
+        mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE);
+        verify(mMockDelegate, times(expectedSuccess ? 1 : 0)).movePip(eq(TEST_MOVE_KEYCODE));
     }
 
     private void assertMenuClosed() {
+        assertMenuClosed(1);
+    }
+
+    private void assertMenuClosed(int times) {
         assertMenuIsOpen(false);
-        verify(mMockDelegate).onMenuClosed();
-        verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_NO_MENU), eq(false));
-        verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_NO_MENU));
+        verify(mMockDelegate, times(times)).onMenuClosed();
+        verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU));
+        verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU));
     }
 
     private void assertMenuIsOpen(boolean open) {
@@ -312,15 +424,10 @@
         assertMenuIsOpen(true);
     }
 
-    private void assertPipMoveSuccessful(boolean expected, boolean actual) {
-        assertTrue("Should " + (expected ? "" : "not ") + "move PiP when the menu is in mode "
-                + mTvPipMenuController.getMenuModeString(), expected == actual);
-    }
-
     private class TestTvPipMenuController extends TvPipMenuController {
 
         TestTvPipMenuController() {
-            super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMockHandler);
+            super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMainHandler);
         }
 
         @Override
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 8191f5e..a958a09 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -15,6 +15,8 @@
  */
 #include "FrameInfo.h"
 
+#include <gui/TraceUtils.h>
+
 #include <cstring>
 
 namespace android {
@@ -51,6 +53,30 @@
 
 void FrameInfo::importUiThreadInfo(int64_t* info) {
     memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
+    mSkippedFrameReason.reset();
+}
+
+const char* toString(SkippedFrameReason reason) {
+    switch (reason) {
+        case SkippedFrameReason::DrawingOff:
+            return "DrawingOff";
+        case SkippedFrameReason::ContextIsStopped:
+            return "ContextIsStopped";
+        case SkippedFrameReason::NothingToDraw:
+            return "NothingToDraw";
+        case SkippedFrameReason::NoOutputTarget:
+            return "NoOutputTarget";
+        case SkippedFrameReason::NoBuffer:
+            return "NoBuffer";
+        case SkippedFrameReason::AlreadyDrawn:
+            return "AlreadyDrawn";
+    }
+}
+
+void FrameInfo::setSkippedFrameReason(android::uirenderer::SkippedFrameReason reason) {
+    ATRACE_FORMAT_INSTANT("Frame skipped: %s", toString(reason));
+    addFlag(FrameInfoFlags::SkippedFrame);
+    mSkippedFrameReason = reason;
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index b15b6cb..f7ad139 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -16,15 +16,17 @@
 #ifndef FRAMEINFO_H_
 #define FRAMEINFO_H_
 
-#include "utils/Macros.h"
-
 #include <cutils/compiler.h>
+#include <memory.h>
 #include <utils/Timers.h>
 
 #include <array>
-#include <memory.h>
+#include <optional>
 #include <string>
 
+#include "SkippedFrameInfo.h"
+#include "utils/Macros.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -186,8 +188,14 @@
         return mFrameInfo[static_cast<int>(index)];
     }
 
+    void setSkippedFrameReason(SkippedFrameReason reason);
+    inline std::optional<SkippedFrameReason> getSkippedFrameReason() const {
+        return mSkippedFrameReason;
+    }
+
 private:
     int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::NumIndexes)];
+    std::optional<SkippedFrameReason> mSkippedFrameReason;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 687e4dd..59f2169 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -148,7 +148,7 @@
     int fast_i = 0, janky_i = 0;
     // Set the bottom of all the shapes to the baseline
     for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
-        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+        if (mFrameSource[fi].getSkippedFrameReason()) {
             continue;
         }
         float lineWidth = baseLineWidth;
@@ -181,7 +181,7 @@
     int janky_i = (mNumJankyRects - 1) * 4;
 
     for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
-        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+        if (mFrameSource[fi].getSkippedFrameReason()) {
             continue;
         }
 
diff --git a/libs/hwui/SkippedFrameInfo.h b/libs/hwui/SkippedFrameInfo.h
new file mode 100644
index 0000000..de56d9a
--- /dev/null
+++ b/libs/hwui/SkippedFrameInfo.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::uirenderer {
+
+enum class SkippedFrameReason {
+    DrawingOff,
+    ContextIsStopped,
+    NothingToDraw,
+    NoOutputTarget,
+    NoBuffer,
+    AlreadyDrawn,
+};
+
+} /* namespace android::uirenderer */
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 2bff9cb..ea25f68 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,14 +16,16 @@
 
 #pragma once
 
-#include "Properties.h"
-#include "utils/Macros.h"
-
 #include <utils/Timers.h>
-#include "SkSize.h"
 
+#include <optional>
 #include <string>
 
+#include "Properties.h"
+#include "SkSize.h"
+#include "SkippedFrameInfo.h"
+#include "utils/Macros.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -110,13 +112,13 @@
         // animate itself, such as if hasFunctors is true
         // This is only set if hasAnimations is true
         bool requiresUiRedraw = false;
-        // This is set to true if draw() can be called this frame
-        // false means that we must delay until the next vsync pulse as frame
+        // This is set to nullopt if draw() can be called this frame
+        // A value means that we must delay until the next vsync pulse as frame
         // production is outrunning consumption
-        // NOTE that if this is false CanvasContext will set either requiresUiRedraw
+        // NOTE that if this has a value CanvasContext will set either requiresUiRedraw
         // *OR* will post itself for the next vsync automatically, use this
         // only to avoid calling draw()
-        bool canDrawThisFrame = true;
+        std::optional<SkippedFrameReason> skippedFrameReason;
         // Sentinel for animatedImageDelay meaning there is no need to post such
         // a message.
         static constexpr nsecs_t kNoAnimatedImageDelay = -1;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 23b3074..530c654 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -17,6 +17,7 @@
 #include "SkiaOpenGLPipeline.h"
 
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/gl/GrGLTypes.h>
 #include <GrBackendSurface.h>
 #include <SkBlendMode.h>
 #include <SkImageInfo.h>
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2ef7802..2e0de3f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -357,8 +357,9 @@
     return true;
 }
 
-static bool wasSkipped(FrameInfo* info) {
-    return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
+static std::optional<SkippedFrameReason> wasSkipped(FrameInfo* info) {
+    if (info) return info->getSkippedFrameReason();
+    return std::nullopt;
 }
 
 bool CanvasContext::isSwapChainStuffed() {
@@ -407,13 +408,26 @@
 
     // If the previous frame was dropped we don't need to hold onto it, so
     // just keep using the previous frame's structure instead
-    if (wasSkipped(mCurrentFrameInfo)) {
+    if (const auto reason = wasSkipped(mCurrentFrameInfo)) {
         // Use the oldest skipped frame in case we skip more than a single frame
         if (!mSkippedFrameInfo) {
-            mSkippedFrameInfo.emplace();
-            mSkippedFrameInfo->vsyncId =
-                mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
-            mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+            switch (*reason) {
+                case SkippedFrameReason::AlreadyDrawn:
+                case SkippedFrameReason::NoBuffer:
+                case SkippedFrameReason::NoOutputTarget:
+                    mSkippedFrameInfo.emplace();
+                    mSkippedFrameInfo->vsyncId =
+                            mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+                    mSkippedFrameInfo->startTime =
+                            mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+                    break;
+                case SkippedFrameReason::DrawingOff:
+                case SkippedFrameReason::ContextIsStopped:
+                case SkippedFrameReason::NothingToDraw:
+                    // Do not report those as skipped frames as there was no frame expected to be
+                    // drawn
+                    break;
+            }
         }
     } else {
         mCurrentFrameInfo = mJankTracker.startFrame();
@@ -427,7 +441,7 @@
     info.damageAccumulator = &mDamageAccumulator;
     info.layerUpdateQueue = &mLayerUpdateQueue;
     info.damageGenerationId = mDamageId++;
-    info.out.canDrawThisFrame = true;
+    info.out.skippedFrameReason = std::nullopt;
 
     mAnimationContext->startFrame(info.mode);
     for (const sp<RenderNode>& node : mRenderNodes) {
@@ -447,8 +461,8 @@
     mIsDirty = true;
 
     if (CC_UNLIKELY(!hasOutputTarget())) {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
-        info.out.canDrawThisFrame = false;
+        info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
+        mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
         return;
     }
 
@@ -463,23 +477,23 @@
         if (vsyncDelta < 2_ms) {
             // Already drew for this vsync pulse, UI draw request missed
             // the deadline for RT animations
-            info.out.canDrawThisFrame = false;
+            info.out.skippedFrameReason = SkippedFrameReason::AlreadyDrawn;
         }
     } else {
-        info.out.canDrawThisFrame = true;
+        info.out.skippedFrameReason = std::nullopt;
     }
 
     // TODO: Do we need to abort out if the backdrop is added but not ready? Should that even
     // be an allowable combination?
     if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) {
-        info.out.canDrawThisFrame = false;
+        info.out.skippedFrameReason = SkippedFrameReason::NothingToDraw;
     }
 
-    if (info.out.canDrawThisFrame) {
+    if (!info.out.skippedFrameReason) {
         int err = mNativeSurface->reserveNext();
         if (err != OK) {
-            mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
-            info.out.canDrawThisFrame = false;
+            info.out.skippedFrameReason = SkippedFrameReason::NoBuffer;
+            mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
             ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err));
             if (err != TIMED_OUT) {
                 // A timed out surface can still recover, but assume others are permanently dead.
@@ -488,11 +502,11 @@
             }
         }
     } else {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+        mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
     }
 
     bool postedFrameCallback = false;
-    if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
+    if (info.out.hasAnimations || info.out.skippedFrameReason) {
         if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
             info.out.requiresUiRedraw = true;
         }
@@ -558,9 +572,20 @@
     mSyncDelayDuration = 0;
     mIdleDuration = 0;
 
-    if (!Properties::isDrawingEnabled() ||
-        (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+    const auto skippedFrameReason = [&]() -> std::optional<SkippedFrameReason> {
+        if (!Properties::isDrawingEnabled()) {
+            return SkippedFrameReason::DrawingOff;
+        }
+
+        if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
+            return SkippedFrameReason::NothingToDraw;
+        }
+
+        return std::nullopt;
+    }();
+    if (skippedFrameReason) {
+        mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason);
+
         if (auto grContext = getGrContext()) {
             // Submit to ensure that any texture uploads complete and Skia can
             // free its staging buffers.
@@ -904,7 +929,7 @@
 
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
     prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
-    if (info.out.canDrawThisFrame) {
+    if (!info.out.skippedFrameReason) {
         draw(info.out.solelyTextureViewUpdates);
     } else {
         // wait on fences so tasks don't overlap next frame
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 53b43ba..1b333bf 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -104,7 +104,7 @@
         info.forceDrawFrame = mForceDrawFrame;
         mForceDrawFrame = false;
         canUnblockUiThread = syncFrameState(info);
-        canDrawThisFrame = info.out.canDrawThisFrame;
+        canDrawThisFrame = !info.out.skippedFrameReason.has_value();
         solelyTextureViewUpdates = info.out.solelyTextureViewUpdates;
 
         if (mFrameCommitCallback) {
@@ -192,11 +192,12 @@
     if (CC_UNLIKELY(!hasTarget || !canDraw)) {
         if (!hasTarget) {
             mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
+            info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
         } else {
             // If we have a surface but can't draw we must be stopped
             mSyncResult |= SyncResult::ContextIsStopped;
+            info.out.skippedFrameReason = SkippedFrameReason::ContextIsStopped;
         }
-        info.out.canDrawThisFrame = false;
     }
 
     if (info.out.hasAnimations) {
@@ -204,7 +205,7 @@
             mSyncResult |= SyncResult::UIRedrawRequired;
         }
     }
-    if (!info.out.canDrawThisFrame) {
+    if (info.out.skippedFrameReason) {
         mSyncResult |= SyncResult::FrameDropped;
     }
     // If prepareTextures is false, we ran out of texture cache space
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 94a061a..d6921c8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -985,6 +985,11 @@
 
     void onRequestCreateControllerByManagerOnHandler(
             RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) {
+        Log.i(
+                TAG,
+                TextUtils.formatSimple(
+                        "requestCreateSessionByManager | requestId: %d, oldSession: %s, route: %s",
+                        managerRequestId, oldSession, route));
         RoutingController controller;
         if (oldSession.isSystemSession()) {
             controller = getSystemController();
diff --git a/packages/SettingsLib/Color/.gitignore b/packages/SettingsLib/Color/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/packages/SettingsLib/Color/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/packages/SettingsLib/Color/Android.bp b/packages/SettingsLib/Color/Android.bp
new file mode 100644
index 0000000..713b7d9
--- /dev/null
+++ b/packages/SettingsLib/Color/Android.bp
@@ -0,0 +1,15 @@
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SettingsLibColor",
+    use_resource_processor: true,
+    sdk_version: "current",
+    min_sdk_version: "28",
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.permission",
+    ],
+}
diff --git a/packages/SettingsLib/Color/AndroidManifest.xml b/packages/SettingsLib/Color/AndroidManifest.xml
new file mode 100644
index 0000000..31e9d23
--- /dev/null
+++ b/packages/SettingsLib/Color/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+
+<manifest package="com.android.settingslib.color" />
diff --git a/packages/SettingsLib/Color/build.gradle.kts b/packages/SettingsLib/Color/build.gradle.kts
new file mode 100644
index 0000000..881bf14
--- /dev/null
+++ b/packages/SettingsLib/Color/build.gradle.kts
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+    alias(libs.plugins.android.library)
+}
+
+android {
+    namespace = "com.android.settingslib.color"
+
+    sourceSets {
+        sourceSets.getByName("main") {
+            res.setSrcDirs(listOf("res"))
+            manifest.srcFile("AndroidManifest.xml")
+        }
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/res/values/colors.xml b/packages/SettingsLib/Color/res/values/colors.xml
similarity index 97%
rename from packages/SettingsLib/Spa/spa/res/values/colors.xml
rename to packages/SettingsLib/Color/res/values/colors.xml
index ca4a0b2..b0b9b10 100644
--- a/packages/SettingsLib/Spa/spa/res/values/colors.xml
+++ b/packages/SettingsLib/Color/res/values/colors.xml
@@ -16,8 +16,6 @@
   -->
 
 <resources>
-    <color name="settingslib_protection_color">@android:color/white</color>
-
     <!-- Dynamic colors-->
     <color name="settingslib_color_blue600">#1a73e8</color>
     <color name="settingslib_color_blue400">#669df6</color>
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index e80eb66..24ccab2 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -14,6 +14,7 @@
     resource_dirs: ["res"],
 
     static_libs: [
+        "SettingsLibColor",
         "androidx.preference_preference",
         "lottie",
     ],
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
index accaa67..e53a43e 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -17,51 +17,4 @@
 
 <resources>
     <color name="settingslib_protection_color">@android:color/white</color>
-
-    <!-- Dynamic colors-->
-    <color name="settingslib_color_blue600">#1a73e8</color>
-    <color name="settingslib_color_blue400">#669df6</color>
-    <color name="settingslib_color_blue300">#8ab4f8</color>
-    <color name="settingslib_color_blue100">#d2e3fc</color>
-    <color name="settingslib_color_blue50">#e8f0fe</color>
-    <color name="settingslib_color_green600">#1e8e3e</color>
-    <color name="settingslib_color_green500">#34A853</color>
-    <color name="settingslib_color_green400">#5bb974</color>
-    <color name="settingslib_color_green100">#ceead6</color>
-    <color name="settingslib_color_green50">#e6f4ea</color>
-    <color name="settingslib_color_red600">#d93025</color>
-    <color name="settingslib_color_red500">#B3261E</color>
-    <color name="settingslib_color_red400">#ee675c</color>
-    <color name="settingslib_color_red100">#fad2cf</color>
-    <color name="settingslib_color_red50">#fce8e6</color>
-    <color name="settingslib_color_yellow600">#f9ab00</color>
-    <color name="settingslib_color_yellow400">#fcc934</color>
-    <color name="settingslib_color_yellow100">#feefc3</color>
-    <color name="settingslib_color_yellow50">#fef7e0</color>
-    <color name="settingslib_color_grey900">#202124</color>
-    <color name="settingslib_color_grey800">#3c4043</color>
-    <color name="settingslib_color_grey700">#5f6368</color>
-    <color name="settingslib_color_grey600">#80868b</color>
-    <color name="settingslib_color_grey500">#9AA0A6</color>
-    <color name="settingslib_color_grey400">#bdc1c6</color>
-    <color name="settingslib_color_grey300">#dadce0</color>
-    <color name="settingslib_color_grey200">#e8eaed</color>
-    <color name="settingslib_color_grey100">#f1f3f4</color>
-    <color name="settingslib_color_grey50">#f8f9fa</color>
-    <color name="settingslib_color_orange600">#e8710a</color>
-    <color name="settingslib_color_orange400">#fa903e</color>
-    <color name="settingslib_color_orange300">#fcad70</color>
-    <color name="settingslib_color_orange100">#fedfc8</color>
-    <color name="settingslib_color_pink600">#e52592</color>
-    <color name="settingslib_color_pink400">#ff63b8</color>
-    <color name="settingslib_color_pink300">#ff8bcb</color>
-    <color name="settingslib_color_pink100">#fdcfe8</color>
-    <color name="settingslib_color_purple600">#9334e6</color>
-    <color name="settingslib_color_purple400">#af5cf7</color>
-    <color name="settingslib_color_purple300">#c58af9</color>
-    <color name="settingslib_color_purple100">#e9d2fd</color>
-    <color name="settingslib_color_cyan600">#12b5c8</color>
-    <color name="settingslib_color_cyan400">#4ecde6</color>
-    <color name="settingslib_color_cyan300">#78d9ec</color>
-    <color name="settingslib_color_cyan100">#cbf0f8</color>
 </resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java
index 07102d5..82b7e04 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java
@@ -23,6 +23,8 @@
 import android.graphics.PorterDuffColorFilter;
 import android.util.Pair;
 
+import com.android.settingslib.color.R;
+
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieProperty;
 import com.airbnb.lottie.model.KeyPath;
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index f166a18..0447ef8 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -21,6 +21,8 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 
+import com.android.settingslib.color.R;
+
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieProperty;
 import com.airbnb.lottie.model.KeyPath;
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index e76139f..1cc2867 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import com.android.build.api.dsl.CommonExtension
 import com.android.build.gradle.BaseExtension
 import com.android.build.gradle.api.AndroidBasePlugin
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -25,7 +26,7 @@
 }
 
 allprojects {
-    extra["jetpackComposeVersion"] = "1.6.0-alpha01"
+    extra["jetpackComposeVersion"] = "1.6.0-alpha02"
 }
 
 subprojects {
@@ -47,10 +48,10 @@
 
     afterEvaluate {
         plugins.withType<AndroidBasePlugin> {
-            configure<BaseExtension> {
+            the(CommonExtension::class).apply {
                 if (buildFeatures.compose == true) {
                     composeOptions {
-                        kotlinCompilerExtensionVersion = "1.4.4"
+                        kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
                     }
                 }
             }
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index ee40b02..0f467b9 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -16,8 +16,9 @@
 
 [versions]
 agp = "8.1.0"
+compose-compiler = "1.5.1"
 dexmaker-mockito = "2.28.3"
-kotlin = "1.8.10"
+kotlin = "1.9.0"
 truth = "1.1"
 
 [libraries]
diff --git a/packages/SettingsLib/Spa/settings.gradle.kts b/packages/SettingsLib/Spa/settings.gradle.kts
index 9909781..aac0fe9 100644
--- a/packages/SettingsLib/Spa/settings.gradle.kts
+++ b/packages/SettingsLib/Spa/settings.gradle.kts
@@ -40,3 +40,5 @@
 include(":spa")
 include(":gallery")
 include(":testutils")
+include(":SettingsLibColor")
+project(":SettingsLibColor").projectDir = File(rootDir, "../Color")
diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp
index 7f5948c..6df0e99 100644
--- a/packages/SettingsLib/Spa/spa/Android.bp
+++ b/packages/SettingsLib/Spa/spa/Android.bp
@@ -24,6 +24,7 @@
     srcs: ["src/**/*.kt"],
     use_resource_processor: true,
     static_libs: [
+        "SettingsLibColor",
         "androidx.slice_slice-builders",
         "androidx.slice_slice-core",
         "androidx.slice_slice-view",
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 377e72ed..84198de 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -52,6 +52,7 @@
 }
 
 dependencies {
+    api(project(":SettingsLibColor"))
     api("androidx.appcompat:appcompat:1.7.0-alpha03")
     api("androidx.slice:slice-builders:1.1.0-alpha02")
     api("androidx.slice:slice-core:1.1.0-alpha02")
@@ -62,7 +63,7 @@
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.7.0-beta01")
+    api("androidx.navigation:navigation-compose:2.7.0-rc01")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
     api("com.google.android.material:material:1.7.0-alpha03")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
index 5f7fe85..a6cc3a9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
@@ -33,7 +33,7 @@
 import com.airbnb.lottie.compose.rememberLottieComposition
 import com.airbnb.lottie.compose.rememberLottieDynamicProperties
 import com.airbnb.lottie.compose.rememberLottieDynamicProperty
-import com.android.settingslib.spa.R
+import com.android.settingslib.color.R
 
 @Composable
 fun Lottie(
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index fe3ef5d..dbc3bf7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioManager;
@@ -520,9 +521,13 @@
                     if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE
                             || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
                             || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) {
-                        MediaDevice mutingExpectedDevice = getMutingExpectedDevice();
-                        if (mutingExpectedDevice != null) {
-                            mMediaDevices.add(mutingExpectedDevice);
+                        if (isTv()) {
+                            mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
+                        } else {
+                            MediaDevice mutingExpectedDevice = getMutingExpectedDevice();
+                            if (mutingExpectedDevice != null) {
+                                mMediaDevices.add(mutingExpectedDevice);
+                            }
                         }
                         break;
                     }
@@ -542,6 +547,12 @@
             }
         }
 
+        private boolean isTv() {
+            PackageManager pm = mContext.getPackageManager();
+            return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+                    || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        }
+
         private MediaDevice getMutingExpectedDevice() {
             if (mBluetoothAdapter == null
                     || mAudioManager.getMutingExpectedDevice() == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 9234d37..147412d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -22,6 +22,7 @@
 import static android.media.MediaRoute2Info.TYPE_GROUP;
 import static android.media.MediaRoute2Info.TYPE_HDMI;
 import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
 import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
@@ -82,7 +83,8 @@
             MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE,
             MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
             MediaDeviceType.TYPE_CAST_DEVICE,
-            MediaDeviceType.TYPE_CAST_GROUP_DEVICE})
+            MediaDeviceType.TYPE_CAST_GROUP_DEVICE,
+            MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER})
     public @interface MediaDeviceType {
         int TYPE_UNKNOWN = 0;
         int TYPE_PHONE_DEVICE = 1;
@@ -92,6 +94,7 @@
         int TYPE_BLUETOOTH_DEVICE = 5;
         int TYPE_CAST_DEVICE = 6;
         int TYPE_CAST_GROUP_DEVICE = 7;
+        int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 8;
     }
 
     @Retention(RetentionPolicy.SOURCE)
@@ -161,6 +164,9 @@
             case TYPE_BLE_HEADSET:
                 mType = MediaDeviceType.TYPE_BLUETOOTH_DEVICE;
                 break;
+            case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
+                mType = MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
+                break;
             case TYPE_UNKNOWN:
             case TYPE_REMOTE_TV:
             case TYPE_REMOTE_SPEAKER:
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index bbfdc38..6c9da97 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -261,6 +261,8 @@
         VALIDATORS.put(Secure.NAV_BAR_KIDS_MODE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 Secure.NAVIGATION_MODE, new DiscreteValueValidator(new String[] {"0", "1", "2"}));
+        VALIDATORS.put(Secure.NAVIGATION_MODE_RESTORE,
+                new DiscreteValueValidator(new String[] {"-1", "0", "1", "2"}));
         VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_LEFT,
                 new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE));
         VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 99a00e4..c830d6b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -835,7 +835,11 @@
                 continue;
             }
 
-            if (settingsToPreserve.contains(getQualifiedKeyForSetting(key, contentUri))) {
+            // Filter out Settings.Secure.NAVIGATION_MODE from modified preserve settings.
+            // Let it take part in restore process. See also b/244532342.
+            boolean isSettingPreserved = settingsToPreserve.contains(
+                    getQualifiedKeyForSetting(key, contentUri));
+            if (isSettingPreserved && !Settings.Secure.NAVIGATION_MODE.equals(key)) {
                 Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as "
                         + "preserved");
                 continue;
@@ -896,6 +900,23 @@
             } else {
                 destination = contentUri;
             }
+
+            // Value is written to NAVIGATION_MODE_RESTORE to mark navigation mode
+            // has been set before on source device.
+            // See also: b/244532342.
+            if (Settings.Secure.NAVIGATION_MODE.equals(key)) {
+                contentValues.clear();
+                contentValues.put(Settings.NameValueTable.NAME,
+                        Settings.Secure.NAVIGATION_MODE_RESTORE);
+                contentValues.put(Settings.NameValueTable.VALUE, value);
+                cr.insert(destination, contentValues);
+                // Avoid restore original setting if it has been preserved.
+                if (isSettingPreserved) {
+                    Log.i(TAG, "Skipping restore for setting navigation_mode "
+                        + "as it is marked as preserved");
+                    continue;
+                }
+            }
             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
                     mRestoredFromSdkInt);
 
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 5475fad..aed6e03 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -859,7 +859,8 @@
                  Settings.Secure.CREDENTIAL_SERVICE,
                  Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
                  Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
-                 Settings.Secure.DND_CONFIGS_MIGRATED);
+                 Settings.Secure.DND_CONFIGS_MIGRATED,
+                 Settings.Secure.NAVIGATION_MODE_RESTORE);
 
     @Test
     public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index d2cb475..4e72518 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -219,4 +219,7 @@
     <!-- Privacy dialog -->
     <item type="id" name="privacy_dialog_close_app_button" />
     <item type="id" name="privacy_dialog_manage_app_button" />
+
+    <!-- Communal mode -->
+    <item type="id" name="communal_widget_wrapper" />
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index f23ae67..4160ae1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -42,7 +42,6 @@
     var keyguardIsVisible: Boolean = false,
     var keyguardOccluded: Boolean = false,
     var occludingAppRequestingFp: Boolean = false,
-    var shouldListenSfpsState: Boolean = false,
     var shouldListenForFingerprintAssistant: Boolean = false,
     var strongerAuthRequired: Boolean = false,
     var switchingUser: Boolean = false,
@@ -74,7 +73,6 @@
             keyguardIsVisible.toString(),
             keyguardOccluded.toString(),
             occludingAppRequestingFp.toString(),
-            shouldListenSfpsState.toString(),
             shouldListenForFingerprintAssistant.toString(),
             strongerAuthRequired.toString(),
             switchingUser.toString(),
@@ -115,7 +113,6 @@
                 keyguardIsVisible = model.keyguardIsVisible
                 keyguardOccluded = model.keyguardOccluded
                 occludingAppRequestingFp = model.occludingAppRequestingFp
-                shouldListenSfpsState = model.shouldListenSfpsState
                 shouldListenForFingerprintAssistant = model.shouldListenForFingerprintAssistant
                 strongerAuthRequired = model.strongerAuthRequired
                 switchingUser = model.switchingUser
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 853a62a..0ba6c05 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -3128,18 +3128,9 @@
                 && !strongerAuthRequired
                 && userDoesNotHaveTrust);
 
-        boolean shouldListenSideFpsState = true;
-        if (isSideFps) {
-            final boolean interactiveToAuthEnabled =
-                    mFingerprintInteractiveToAuthProvider != null &&
-                            mFingerprintInteractiveToAuthProvider.isEnabled(getCurrentUser());
-            shouldListenSideFpsState =
-                    interactiveToAuthEnabled ? isDeviceInteractive() && !mGoingToSleep : true;
-        }
 
         boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
-                && shouldListenBouncerState && shouldListenUdfpsState
-                && shouldListenSideFpsState;
+                && shouldListenBouncerState && shouldListenUdfpsState;
         logListenerModelData(
                 new KeyguardFingerprintListenModel(
                     System.currentTimeMillis(),
@@ -3160,7 +3151,6 @@
                     isKeyguardVisible(),
                     mKeyguardOccluded,
                     mOccludingAppRequestingFp,
-                    shouldListenSideFpsState,
                     shouldListenForFingerprintAssistant,
                     strongerAuthRequired,
                     mSwitchingUser,
@@ -3314,6 +3304,9 @@
                         mFingerprintDetectionCallback,
                         new FingerprintAuthenticateOptions.Builder()
                                 .setUserId(userId)
+                                .setVendorReason(
+                                        mFingerprintInteractiveToAuthProvider.getVendorExtension(
+                                                getCurrentUser()))
                                 .build());
             } else {
                 mLogger.v("startListeningForFingerprint");
@@ -3322,6 +3315,9 @@
                         null /* handler */,
                         new FingerprintAuthenticateOptions.Builder()
                                 .setUserId(userId)
+                                .setVendorReason(
+                                        mFingerprintInteractiveToAuthProvider.getVendorExtension(
+                                                getCurrentUser()))
                                 .build()
                 );
             }
@@ -4497,14 +4493,6 @@
             } else if (isSfpsSupported()) {
                 pw.println("        sfpsEnrolled=" + isSfpsEnrolled());
                 pw.println("        shouldListenForSfps=" + shouldListenForFingerprint(false));
-                if (isSfpsEnrolled()) {
-                    final boolean interactiveToAuthEnabled =
-                                    mFingerprintInteractiveToAuthProvider != null &&
-                                            mFingerprintInteractiveToAuthProvider
-                                            .isEnabled(getCurrentUser());
-                    pw.println("        interactiveToAuthEnabled="
-                            + interactiveToAuthEnabled);
-                }
             }
             new DumpsysTableLogger(
                     "KeyguardFingerprintListen",
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index c28a9eb..340bf3f 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -103,7 +103,8 @@
             AppOpsManager.OP_RECORD_AUDIO,
             AppOpsManager.OP_PHONE_CALL_MICROPHONE,
             AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
-            AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
+            AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+            AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO
     };
 
     protected static final int[] OPS_CAMERA = new int[] {
@@ -118,8 +119,7 @@
     };
 
     protected static final int[] OPS_OTHERS = new int[] {
-            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-            AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO
+            AppOpsManager.OP_SYSTEM_ALERT_WINDOW
     };
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java
index 902bb18..4bc07e8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintInteractiveToAuthProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.biometrics;
 
+import android.hardware.biometrics.common.AuthenticateReason;
+
 /** Provides the status of the interactive to auth feature. */
 public interface FingerprintInteractiveToAuthProvider {
     /**
@@ -24,4 +26,11 @@
      * @return true if the InteractiveToAuthFeature is enabled, false if disabled.
      */
     boolean isEnabled(int userId);
+
+    /**
+     *
+     * @param userId the user Id.
+     * @return Vendor extension if needed for authentication.
+     */
+    AuthenticateReason.Vendor getVendorExtension(int userId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
new file mode 100644
index 0000000..e2a7d07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.communal.data.repository
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.os.UserManager
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates the state of widgets for communal mode. */
+interface CommunalWidgetRepository {
+    /** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
+    val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
+}
+
+@SysUISingleton
+class CommunalWidgetRepositoryImpl
+@Inject
+constructor(
+    private val appWidgetManager: AppWidgetManager,
+    private val appWidgetHost: AppWidgetHost,
+    broadcastDispatcher: BroadcastDispatcher,
+    private val packageManager: PackageManager,
+    private val userManager: UserManager,
+    private val userTracker: UserTracker,
+    @CommunalLog logBuffer: LogBuffer,
+    featureFlags: FeatureFlags,
+) : CommunalWidgetRepository {
+    companion object {
+        const val TAG = "CommunalWidgetRepository"
+        const val WIDGET_LABEL = "Stopwatch"
+    }
+
+    private val logger = Logger(logBuffer, TAG)
+
+    // Whether the [AppWidgetHost] is listening for updates.
+    private var isHostListening = false
+
+    // Widgets that should be rendered in communal mode.
+    private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
+
+    private val isUserUnlocked: Flow<Boolean> = callbackFlow {
+        if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
+            awaitClose()
+        }
+
+        fun isUserUnlockingOrUnlocked(): Boolean {
+            return userManager.isUserUnlockingOrUnlocked(userTracker.userHandle)
+        }
+
+        fun send() {
+            trySendWithFailureLogging(isUserUnlockingOrUnlocked(), TAG)
+        }
+
+        if (isUserUnlockingOrUnlocked()) {
+            send()
+            awaitClose()
+        } else {
+            val receiver =
+                object : BroadcastReceiver() {
+                    override fun onReceive(context: Context?, intent: Intent?) {
+                        send()
+                    }
+                }
+
+            broadcastDispatcher.registerReceiver(
+                receiver,
+                IntentFilter(Intent.ACTION_USER_UNLOCKED),
+            )
+
+            awaitClose { broadcastDispatcher.unregisterReceiver(receiver) }
+        }
+    }
+
+    override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> =
+        isUserUnlocked.map { isUserUnlocked ->
+            if (!isUserUnlocked) {
+                clearWidgets()
+                stopListening()
+                return@map null
+            }
+
+            startListening()
+
+            val providerInfo =
+                appWidgetManager.installedProviders.find {
+                    it.loadLabel(packageManager).equals(WIDGET_LABEL)
+                }
+
+            if (providerInfo == null) {
+                logger.w("Cannot find app widget: $WIDGET_LABEL")
+                return@map null
+            }
+
+            return@map addWidget(providerInfo)
+        }
+
+    private fun startListening() {
+        if (isHostListening) {
+            return
+        }
+
+        appWidgetHost.startListening()
+        isHostListening = true
+    }
+
+    private fun stopListening() {
+        if (!isHostListening) {
+            return
+        }
+
+        appWidgetHost.stopListening()
+        isHostListening = false
+    }
+
+    private fun addWidget(providerInfo: AppWidgetProviderInfo): CommunalAppWidgetInfo {
+        val existing = widgets.values.firstOrNull { it.providerInfo == providerInfo }
+        if (existing != null) {
+            return existing
+        }
+
+        val appWidgetId = appWidgetHost.allocateAppWidgetId()
+        val widget =
+            CommunalAppWidgetInfo(
+                providerInfo,
+                appWidgetId,
+            )
+        widgets[appWidgetId] = widget
+        return widget
+    }
+
+    private fun clearWidgets() {
+        widgets.keys.forEach { appWidgetId -> appWidgetHost.deleteAppWidgetId(appWidgetId) }
+        widgets.clear()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
new file mode 100644
index 0000000..3d1185b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.communal.data.repository
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface CommunalWidgetRepositoryModule {
+    companion object {
+        private const val APP_WIDGET_HOST_ID = 116
+
+        @SysUISingleton
+        @Provides
+        fun provideAppWidgetManager(@Application context: Context): AppWidgetManager {
+            return AppWidgetManager.getInstance(context)
+        }
+
+        @SysUISingleton
+        @Provides
+        fun provideAppWidgetHost(@Application context: Context): AppWidgetHost {
+            return AppWidgetHost(context, APP_WIDGET_HOST_ID)
+        }
+    }
+
+    @Binds
+    fun communalWidgetRepository(impl: CommunalWidgetRepositoryImpl): CommunalWidgetRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
new file mode 100644
index 0000000..6dc305e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.CommunalWidgetRepository
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Encapsulates business-logic related to communal mode. */
+@SysUISingleton
+class CommunalInteractor
+@Inject
+constructor(
+    widgetRepository: CommunalWidgetRepository,
+) {
+    /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
+    val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
new file mode 100644
index 0000000..0803a01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.communal.shared
+
+import android.appwidget.AppWidgetProviderInfo
+
+/** A data class that stores info about an app widget that displays in communal mode. */
+data class CommunalAppWidgetInfo(
+    val providerInfo: AppWidgetProviderInfo,
+    val appWidgetId: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
new file mode 100644
index 0000000..2a08d7f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.communal.ui.adapter
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.content.Context
+import android.util.SizeF
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Transforms a [CommunalAppWidgetInfo] to a view that renders the widget. */
+class CommunalWidgetViewAdapter
+@Inject
+constructor(
+    @Application private val context: Context,
+    private val appWidgetManager: AppWidgetManager,
+    private val appWidgetHost: AppWidgetHost,
+    @CommunalLog logBuffer: LogBuffer,
+) {
+    companion object {
+        private const val TAG = "CommunalWidgetViewAdapter"
+    }
+
+    private val logger = Logger(logBuffer, TAG)
+
+    fun adapt(providerInfoFlow: Flow<CommunalAppWidgetInfo?>): Flow<CommunalWidgetWrapper?> =
+        providerInfoFlow.map {
+            if (it == null) {
+                return@map null
+            }
+
+            val appWidgetId = it.appWidgetId
+            val providerInfo = it.providerInfo
+
+            if (appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, providerInfo.provider)) {
+                logger.d("Success binding app widget id: $appWidgetId")
+                return@map CommunalWidgetWrapper(context).apply {
+                    addView(
+                        appWidgetHost.createView(context, appWidgetId, providerInfo).apply {
+                            // Set the widget to minimum width and height
+                            updateAppWidgetSize(
+                                appWidgetManager.getAppWidgetOptions(appWidgetId),
+                                listOf(
+                                    SizeF(
+                                        providerInfo.minResizeWidth.toFloat(),
+                                        providerInfo.minResizeHeight.toFloat()
+                                    )
+                                )
+                            )
+                        }
+                    )
+                }
+            } else {
+                logger.w("Failed binding app widget id")
+                return@map null
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
new file mode 100644
index 0000000..1b6d3a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.communal.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
+import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
+import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.launch
+
+/** Binds [CommunalWidgetViewModel] to the keyguard root view. */
+object CommunalWidgetViewBinder {
+
+    @JvmStatic
+    fun bind(
+        rootView: KeyguardRootView,
+        viewModel: CommunalWidgetViewModel,
+        adapter: CommunalWidgetViewAdapter,
+        keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
+    ) {
+        rootView.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    adapter.adapt(viewModel.appWidgetInfo).collect {
+                        val oldView =
+                            rootView.findViewById<CommunalWidgetWrapper>(
+                                R.id.communal_widget_wrapper
+                            )
+                        var dirty = false
+
+                        if (oldView != null) {
+                            rootView.removeView(oldView)
+                            dirty = true
+                        }
+
+                        if (it != null) {
+                            rootView.addView(it)
+                            dirty = true
+                        }
+
+                        if (dirty) {
+                            keyguardBlueprintInteractor.refreshBlueprint()
+                        }
+                    }
+                }
+
+                launch { viewModel.alpha.collect { rootView.alpha = it } }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt
new file mode 100644
index 0000000..560f4fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/CommunalWidgetWrapper.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.communal.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import com.android.systemui.R
+
+/** Wraps around a widget rendered in communal mode. */
+class CommunalWidgetWrapper(context: Context, attrs: AttributeSet? = null) :
+    LinearLayout(context, attrs) {
+    init {
+        id = R.id.communal_widget_wrapper
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
new file mode 100644
index 0000000..c3369da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.communal.ui.view.layout.blueprints
+
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import javax.inject.Inject
+
+/** Blueprint for communal mode. */
+@SysUISingleton
+@JvmSuppressWildcards
+class DefaultCommunalBlueprint
+@Inject
+constructor(
+    private val defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
+) : KeyguardBlueprint {
+    override val id: String = COMMUNAL
+
+    override fun apply(constraintSet: ConstraintSet) {
+        defaultCommunalWidgetSection.apply(constraintSet)
+    }
+
+    companion object {
+        const val COMMUNAL = "communal"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
new file mode 100644
index 0000000..b0e3132
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.communal.ui.view.layout.sections
+
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import com.android.systemui.R
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import javax.inject.Inject
+
+class DefaultCommunalWidgetSection @Inject constructor() : KeyguardSection {
+    private val widgetAreaViewId = R.id.communal_widget_wrapper
+
+    override fun apply(constraintSet: ConstraintSet) {
+        constraintSet.apply {
+            constrainWidth(widgetAreaViewId, WRAP_CONTENT)
+            constrainHeight(widgetAreaViewId, WRAP_CONTENT)
+            connect(widgetAreaViewId, BOTTOM, PARENT_ID, BOTTOM)
+            connect(widgetAreaViewId, END, PARENT_ID, END)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
new file mode 100644
index 0000000..8fba342
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.communal.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class CommunalWidgetViewModel
+@Inject
+constructor(
+    communalInteractor: CommunalInteractor,
+    keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
+) {
+    /** An observable for the alpha level for the communal widget area. */
+    val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
+
+    /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
+    val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = communalInteractor.appWidgetInfo
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 8e323d8..9b323ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -24,9 +24,13 @@
 import com.android.keyguard.dagger.KeyguardStatusViewComponent
 import com.android.systemui.CoreStartable
 import com.android.systemui.R
+import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
+import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
+import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
@@ -83,6 +87,9 @@
     private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
     private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
     private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+    private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
+    private val communalWidgetViewModel: CommunalWidgetViewModel,
+    private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
 ) : CoreStartable {
 
     private var rootViewHandle: DisposableHandle? = null
@@ -106,6 +113,7 @@
         bindRightShortcut()
         bindAmbientIndicationArea()
         bindSettingsPopupMenu()
+        bindCommunalWidgetArea()
 
         KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
         keyguardBlueprintCommandListener.start()
@@ -279,6 +287,19 @@
         }
     }
 
+    private fun bindCommunalWidgetArea() {
+        if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
+            return
+        }
+
+        CommunalWidgetViewBinder.bind(
+            keyguardRootView,
+            communalWidgetViewModel,
+            communalWidgetViewAdapter,
+            keyguardBlueprintInteractor,
+        )
+    }
+
     /**
      * Temporary, to allow NotificationPanelViewController to use the same instance while code is
      * migrated: b/288242803
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 9a44230..13dfe24 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -37,6 +37,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingModule;
+import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -75,11 +76,12 @@
 import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
 import com.android.wm.shell.keyguard.KeyguardTransitions;
 
-import java.util.concurrent.Executor;
-
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
+
+import java.util.concurrent.Executor;
+
 import kotlinx.coroutines.CoroutineDispatcher;
 
 /**
@@ -91,6 +93,7 @@
         KeyguardStatusViewComponent.class,
         KeyguardUserSwitcherComponent.class},
         includes = {
+            CommunalWidgetRepositoryModule.class,
             FalsingModule.class,
             KeyguardDataQuickAffordanceModule.class,
             KeyguardRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index fefe679..07f316b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.ui.view.layout.blueprints
 
+import com.android.systemui.communal.ui.view.layout.blueprints.DefaultCommunalBlueprint
 import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
 import dagger.Binds
 import dagger.Module
@@ -35,4 +36,10 @@
     abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
         shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
     ): KeyguardBlueprint
+
+    @Binds
+    @IntoSet
+    abstract fun bindDefaultCommunalBlueprint(
+        defaultCommunalBlueprint: DefaultCommunalBlueprint
+    ): KeyguardBlueprint
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt
new file mode 100644
index 0000000..afb18f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalLog.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for communal-related logging. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class CommunalLog
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 b6577f7..5127d14 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -497,6 +497,16 @@
         return factory.create("DreamLog", 250);
     }
 
+    /**
+     * Provides a {@link LogBuffer} for communal-related logs.
+     */
+    @Provides
+    @SysUISingleton
+    @CommunalLog
+    public static LogBuffer provideCommunalLogBuffer(LogBufferFactory factory) {
+        return factory.create("CommunalLog", 250);
+    }
+
     /** Provides a {@link LogBuffer} for display metrics related logs. */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 39d4e6e..412d1a3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -56,6 +56,7 @@
      * Update the endpoints of a content switching operation.
      * This method should be called before a switching operation, so the metric logger can track
      * source and target devices.
+     *
      * @param source the current connected media device
      * @param target the target media device for content switching to
      */
@@ -72,37 +73,9 @@
 
     /**
      * Do the metric logging of content switching success.
+     *
      * @param selectedDeviceType string representation of the target media device
-     * @param deviceList media device list for device count updating
-     */
-    public void logOutputSuccess(String selectedDeviceType, List<MediaDevice> deviceList) {
-        if (DEBUG) {
-            Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType);
-        }
-
-        if (mSourceDevice == null && mTargetDevice == null) {
-            return;
-        }
-
-        updateLoggingDeviceCount(deviceList);
-
-        SysUiStatsLog.write(
-                SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
-                getLoggingDeviceType(mSourceDevice, true),
-                getLoggingDeviceType(mTargetDevice, false),
-                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK,
-                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR,
-                getLoggingPackageName(),
-                mWiredDeviceCount,
-                mConnectedBluetoothDeviceCount,
-                mRemoteDeviceCount,
-                mAppliedDeviceCountWithinRemoteGroup);
-    }
-
-    /**
-     * Do the metric logging of content switching success.
-     * @param selectedDeviceType string representation of the target media device
-     * @param deviceItemList media item list for device count updating
+     * @param deviceItemList     media item list for device count updating
      */
     public void logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList) {
         if (DEBUG) {
@@ -125,11 +98,14 @@
                 mWiredDeviceCount,
                 mConnectedBluetoothDeviceCount,
                 mRemoteDeviceCount,
-                mAppliedDeviceCountWithinRemoteGroup);
+                mAppliedDeviceCountWithinRemoteGroup,
+                mTargetDevice.isSuggestedDevice(),
+                mTargetDevice.hasOngoingSession());
     }
 
     /**
      * Do the metric logging of volume adjustment.
+     *
      * @param source the device been adjusted
      */
     public void logInteractionAdjustVolume(MediaDevice source) {
@@ -141,7 +117,8 @@
                 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
                 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__ADJUST_VOLUME,
                 getInteractionDeviceType(source),
-                getLoggingPackageName());
+                getLoggingPackageName(),
+                source.isSuggestedDevice());
     }
 
     /**
@@ -156,7 +133,8 @@
                 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
                 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__STOP_CASTING,
                 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE,
-                getLoggingPackageName());
+                getLoggingPackageName(),
+                /*isSuggestedDevice = */false);
     }
 
     /**
@@ -171,42 +149,15 @@
                 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
                 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__EXPANSION,
                 getInteractionDeviceType(source),
-                getLoggingPackageName());
-    }
-
-    /**
-     * Do the metric logging of content switching failure.
-     * @param deviceList media device list for device count updating
-     * @param reason the reason of content switching failure
-     */
-    public void logOutputFailure(List<MediaDevice> deviceList, int reason) {
-        if (DEBUG) {
-            Log.e(TAG, "logRequestFailed - " + reason);
-        }
-
-        if (mSourceDevice == null && mTargetDevice == null) {
-            return;
-        }
-
-        updateLoggingDeviceCount(deviceList);
-
-        SysUiStatsLog.write(
-                SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
-                getLoggingDeviceType(mSourceDevice, true),
-                getLoggingDeviceType(mTargetDevice, false),
-                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR,
-                getLoggingSwitchOpSubResult(reason),
                 getLoggingPackageName(),
-                mWiredDeviceCount,
-                mConnectedBluetoothDeviceCount,
-                mRemoteDeviceCount,
-                mAppliedDeviceCountWithinRemoteGroup);
+                source.isSuggestedDevice());
     }
 
     /**
      * Do the metric logging of content switching failure.
+     *
      * @param deviceItemList media item list for device count updating
-     * @param reason the reason of content switching failure
+     * @param reason         the reason of content switching failure
      */
     public void logOutputItemFailure(List<MediaItem> deviceItemList, int reason) {
         if (DEBUG) {
@@ -229,7 +180,9 @@
                 mWiredDeviceCount,
                 mConnectedBluetoothDeviceCount,
                 mRemoteDeviceCount,
-                mAppliedDeviceCountWithinRemoteGroup);
+                mAppliedDeviceCountWithinRemoteGroup,
+                mTargetDevice.isSuggestedDevice(),
+                mTargetDevice.hasOngoingSession());
     }
 
     private void updateLoggingDeviceCount(List<MediaDevice> deviceList) {
@@ -266,7 +219,7 @@
         mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0;
         mAppliedDeviceCountWithinRemoteGroup = 0;
 
-        for (MediaItem mediaItem: deviceItemList) {
+        for (MediaItem mediaItem : deviceItemList) {
             if (mediaItem.getMediaDevice().isPresent()
                     && mediaItem.getMediaDevice().get().isConnected()) {
                 switch (mediaItem.getMediaDevice().get().getDeviceType()) {
@@ -326,6 +279,10 @@
                 return isSourceDevice
                         ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_GROUP
                         : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_GROUP;
+            case MediaDevice.MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
+                return isSourceDevice
+                        ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__AVR
+                        : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__AVR;
             default:
                 return isSourceDevice
                         ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 316b54e..9dca013 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -133,6 +133,7 @@
             setShowForAllUsers(true);
             mWallpaperLocalColorExtractor = new WallpaperLocalColorExtractor(
                     mLongExecutor,
+                    mLock,
                     new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
                         @Override
                         public void onColorsProcessed(List<RectF> regions,
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
index 1e8446f..e2ec8dc 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
@@ -61,7 +61,7 @@
     private int mBitmapWidth = -1;
     private int mBitmapHeight = -1;
 
-    private final Object mLock = new Object();
+    private final Object mLock;
 
     private final List<RectF> mPendingRegions = new ArrayList<>();
     private final Set<RectF> mProcessedRegions = new ArraySet<>();
@@ -102,12 +102,15 @@
     /**
      * Creates a new color extractor.
      * @param longExecutor the executor on which the color extraction will be performed
+     * @param lock the lock object to use for computing colors or processing the bitmap
      * @param wallpaperLocalColorExtractorCallback an interface to handle the callbacks from
      *                                        the color extractor.
      */
     public WallpaperLocalColorExtractor(@LongRunning Executor longExecutor,
+            Object lock,
             WallpaperLocalColorExtractorCallback wallpaperLocalColorExtractorCallback) {
         mLongExecutor = longExecutor;
+        mLock = lock;
         mWallpaperLocalColorExtractorCallback = wallpaperLocalColorExtractorCallback;
     }
 
@@ -149,6 +152,12 @@
 
     private void onBitmapChangedSynchronized(@NonNull Bitmap bitmap) {
         synchronized (mLock) {
+            if (bitmap.isRecycled()) {
+                // ImageWallpaper loaded a new bitmap before the extraction of the previous one
+                // In that case, ImageWallpaper also scheduled the extraction of the next bitmap
+                Log.i(TAG, "Wallpaper has changed; deferring color extraction");
+                return;
+            }
             if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
                 Log.e(TAG, "Attempt to extract colors from an invalid bitmap");
                 return;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6f3322a..0cd82f0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -1694,71 +1694,6 @@
     }
 
     @Test
-    public void startsListeningForSfps_whenKeyguardIsVisible_ifRequireInteractiveToAuthEnabled()
-            throws RemoteException {
-        // SFPS supported and enrolled
-        when(mAuthController.isSfpsSupported()).thenReturn(true);
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-
-        // WHEN require interactive to auth is disabled, and keyguard is not awake
-        when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(false);
-
-        // Preconditions for sfps auth to run
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-
-        statusBarShadeIsLocked();
-        mTestableLooper.processAllMessages();
-
-        // THEN we should listen for sfps when screen off, because require screen on is disabled
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
-
-        // WHEN require interactive to auth is enabled, and keyguard is not awake
-        when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(true);
-
-        // THEN we shouldn't listen for sfps when screen off, because require screen on is enabled
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
-
-        // Device now awake & keyguard is now interactive
-        deviceNotGoingToSleep();
-        deviceIsInteractive();
-        keyguardIsVisible();
-
-        // THEN we should listen for sfps when screen on, and require screen on is enabled
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
-    }
-
-    @Test
-    public void notListeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthEnabled()
-            throws RemoteException {
-        // GIVEN SFPS supported and enrolled
-        when(mAuthController.isSfpsSupported()).thenReturn(true);
-        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
-
-        // GIVEN Preconditions for sfps auth to run
-        keyguardNotGoingAway();
-        currentUserIsSystem();
-        currentUserDoesNotHaveTrust();
-        biometricsNotDisabledThroughDevicePolicyManager();
-        biometricsEnabledForCurrentUser();
-        userNotCurrentlySwitching();
-        statusBarShadeIsLocked();
-
-        // WHEN require interactive to auth is enabled & keyguard is going to sleep
-        when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(true);
-        deviceGoingToSleep();
-
-        mTestableLooper.processAllMessages();
-
-        // THEN we should NOT listen for sfps because device is going to sleep
-        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
-    }
-
-    @Test
     public void listeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthDisabled()
             throws RemoteException {
         // GIVEN SFPS supported and enrolled
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
new file mode 100644
index 0000000..3df9cbb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -0,0 +1,285 @@
+package com.android.systemui.communal.data.repository
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.BroadcastReceiver
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
+    @Mock private lateinit var appWidgetManager: AppWidgetManager
+
+    @Mock private lateinit var appWidgetHost: AppWidgetHost
+
+    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+
+    @Mock private lateinit var packageManager: PackageManager
+
+    @Mock private lateinit var userManager: UserManager
+
+    @Mock private lateinit var userHandle: UserHandle
+
+    @Mock private lateinit var userTracker: UserTracker
+
+    @Mock private lateinit var featureFlags: FeatureFlags
+
+    @Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
+
+    private lateinit var logBuffer: LogBuffer
+
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        logBuffer = FakeLogBuffer.Factory.create()
+
+        featureFlagEnabled(true)
+        whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
+        whenever(userTracker.userHandle).thenReturn(userHandle)
+    }
+
+    @Test
+    fun broadcastReceiver_featureDisabled_doNotRegisterUserUnlockedBroadcastReceiver() =
+        testScope.runTest {
+            featureFlagEnabled(false)
+            val repository = initCommunalWidgetRepository()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            verifyBroadcastReceiverNeverRegistered()
+        }
+
+    @Test
+    fun broadcastReceiver_featureEnabledAndUserUnlocked_doNotRegisterBroadcastReceiver() =
+        testScope.runTest {
+            userUnlocked(true)
+            val repository = initCommunalWidgetRepository()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            verifyBroadcastReceiverNeverRegistered()
+        }
+
+    @Test
+    fun broadcastReceiver_featureEnabledAndUserLocked_registerBroadcastReceiver() =
+        testScope.runTest {
+            userUnlocked(false)
+            val repository = initCommunalWidgetRepository()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            verifyBroadcastReceiverRegistered()
+        }
+
+    @Test
+    fun broadcastReceiver_whenFlowFinishes_unregisterBroadcastReceiver() =
+        testScope.runTest {
+            userUnlocked(false)
+            val repository = initCommunalWidgetRepository()
+
+            val job = launch { repository.stopwatchAppWidgetInfo.collect() }
+            runCurrent()
+            val receiver = broadcastReceiverUpdate()
+
+            job.cancel()
+            runCurrent()
+
+            Mockito.verify(broadcastDispatcher).unregisterReceiver(receiver)
+        }
+
+    @Test
+    fun stopwatch_whenUserUnlocks_receiveProviderInfo() =
+        testScope.runTest {
+            userUnlocked(false)
+            val repository = initCommunalWidgetRepository()
+            val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
+            assertThat(lastStopwatchProviderInfo()).isNull()
+
+            userUnlocked(true)
+            installedProviders(listOf(stopwatchProviderInfo))
+            broadcastReceiverUpdate()
+
+            assertThat(lastStopwatchProviderInfo()?.providerInfo).isEqualTo(stopwatchProviderInfo)
+        }
+
+    @Test
+    fun stopwatch_userUnlockedButWidgetNotInstalled_noProviderInfo() =
+        testScope.runTest {
+            userUnlocked(true)
+            installedProviders(listOf())
+
+            val repository = initCommunalWidgetRepository()
+
+            val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
+            assertThat(lastStopwatchProviderInfo()).isNull()
+        }
+
+    @Test
+    fun appWidgetId_providerInfoAvailable_allocateAppWidgetId() =
+        testScope.runTest {
+            userUnlocked(true)
+            installedProviders(listOf(stopwatchProviderInfo))
+            val repository = initCommunalWidgetRepository()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            Mockito.verify(appWidgetHost).allocateAppWidgetId()
+        }
+
+    @Test
+    fun appWidgetId_userLockedAgainAfterProviderInfoAvailable_deleteAppWidgetId() =
+        testScope.runTest {
+            whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(123456)
+            userUnlocked(false)
+            val repository = initCommunalWidgetRepository()
+            val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
+            assertThat(lastStopwatchProviderInfo()).isNull()
+
+            // User unlocks
+            userUnlocked(true)
+            installedProviders(listOf(stopwatchProviderInfo))
+            broadcastReceiverUpdate()
+
+            // Verify app widget id allocated
+            assertThat(lastStopwatchProviderInfo()?.appWidgetId).isEqualTo(123456)
+            Mockito.verify(appWidgetHost).allocateAppWidgetId()
+            Mockito.verify(appWidgetHost, Mockito.never()).deleteAppWidgetId(anyInt())
+
+            // User locked again
+            userUnlocked(false)
+            broadcastReceiverUpdate()
+
+            // Verify app widget id deleted
+            assertThat(lastStopwatchProviderInfo()).isNull()
+            Mockito.verify(appWidgetHost).deleteAppWidgetId(123456)
+        }
+
+    @Test
+    fun appWidgetHost_userUnlocked_startListening() =
+        testScope.runTest {
+            userUnlocked(false)
+            val repository = initCommunalWidgetRepository()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            Mockito.verify(appWidgetHost, Mockito.never()).startListening()
+
+            userUnlocked(true)
+            broadcastReceiverUpdate()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+
+            Mockito.verify(appWidgetHost).startListening()
+        }
+
+    @Test
+    fun appWidgetHost_userLockedAgain_stopListening() =
+        testScope.runTest {
+            userUnlocked(false)
+            val repository = initCommunalWidgetRepository()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+
+            userUnlocked(true)
+            broadcastReceiverUpdate()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+
+            Mockito.verify(appWidgetHost).startListening()
+            Mockito.verify(appWidgetHost, Mockito.never()).stopListening()
+
+            userUnlocked(false)
+            broadcastReceiverUpdate()
+            collectLastValue(repository.stopwatchAppWidgetInfo)()
+
+            Mockito.verify(appWidgetHost).stopListening()
+        }
+
+    private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
+        return CommunalWidgetRepositoryImpl(
+            appWidgetManager,
+            appWidgetHost,
+            broadcastDispatcher,
+            packageManager,
+            userManager,
+            userTracker,
+            logBuffer,
+            featureFlags,
+        )
+    }
+
+    private fun verifyBroadcastReceiverRegistered() {
+        Mockito.verify(broadcastDispatcher)
+            .registerReceiver(
+                any(),
+                any(),
+                nullable(),
+                nullable(),
+                anyInt(),
+                nullable(),
+            )
+    }
+
+    private fun verifyBroadcastReceiverNeverRegistered() {
+        Mockito.verify(broadcastDispatcher, Mockito.never())
+            .registerReceiver(
+                any(),
+                any(),
+                nullable(),
+                nullable(),
+                anyInt(),
+                nullable(),
+            )
+    }
+
+    private fun broadcastReceiverUpdate(): BroadcastReceiver {
+        val broadcastReceiverCaptor = kotlinArgumentCaptor<BroadcastReceiver>()
+        Mockito.verify(broadcastDispatcher)
+            .registerReceiver(
+                broadcastReceiverCaptor.capture(),
+                any(),
+                nullable(),
+                nullable(),
+                anyInt(),
+                nullable(),
+            )
+        broadcastReceiverCaptor.value.onReceive(null, null)
+        return broadcastReceiverCaptor.value
+    }
+
+    private fun featureFlagEnabled(enabled: Boolean) {
+        whenever(featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)).thenReturn(enabled)
+    }
+
+    private fun userUnlocked(userUnlocked: Boolean) {
+        whenever(userManager.isUserUnlockingOrUnlocked(userHandle)).thenReturn(userUnlocked)
+    }
+
+    private fun installedProviders(providers: List<AppWidgetProviderInfo>) {
+        whenever(appWidgetManager.installedProviders).thenReturn(providers)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
new file mode 100644
index 0000000..d28f530
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.communal.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RoboPilotTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class CommunalInteractorTest : SysuiTestCase() {
+    @Mock private lateinit var stopwatchAppWidgetInfo: CommunalAppWidgetInfo
+
+    private lateinit var testScope: TestScope
+
+    private lateinit var widgetRepository: FakeCommunalWidgetRepository
+    private lateinit var interactor: CommunalInteractor
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        testScope = TestScope()
+        widgetRepository = FakeCommunalWidgetRepository()
+        interactor = CommunalInteractor(widgetRepository)
+    }
+
+    @Test
+    fun testAppWidgetInfoFlow() =
+        testScope.runTest {
+            val lastAppWidgetInfo = collectLastValue(interactor.appWidgetInfo)
+            runCurrent()
+            assertThat(lastAppWidgetInfo()).isNull()
+
+            widgetRepository.setStopwatchAppWidgetInfo(stopwatchAppWidgetInfo)
+            runCurrent()
+            assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
new file mode 100644
index 0000000..e3a75f1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -0,0 +1,36 @@
+package com.android.systemui.communal.ui.view.layout.blueprints
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class DefaultCommunalBlueprintTest : SysuiTestCase() {
+    @Mock private lateinit var widgetSection: DefaultCommunalWidgetSection
+
+    private lateinit var blueprint: DefaultCommunalBlueprint
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        blueprint = DefaultCommunalBlueprint(widgetSection)
+    }
+
+    @Test
+    fun apply() {
+        val cs = ConstraintSet()
+        blueprint.apply(cs)
+        verify(widgetSection).apply(cs)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
index fc5f782..1125d41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
@@ -109,6 +109,7 @@
 
         WallpaperLocalColorExtractor colorExtractor = new WallpaperLocalColorExtractor(
                 mBackgroundExecutor,
+                new Object(),
                 new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
                     @Override
                     public void onColorsProcessed(List<RectF> regions,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
new file mode 100644
index 0000000..1a8c583
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -0,0 +1,15 @@
+package com.android.systemui.communal.data.repository
+
+import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [CommunalWidgetRepository] */
+class FakeCommunalWidgetRepository : CommunalWidgetRepository {
+    private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
+    override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
+
+    fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
+        _stopwatchAppWidgetInfo.value = appWidgetInfo
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 9b9593b..8060d5a 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -637,7 +637,7 @@
             MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         switch (event.getActionMasked()) {
             case ACTION_DOWN:
-                // We should have already received ACTION_DOWN. Ignore.
+                handleActionDownStateTouchExploring(event, rawEvent, policyFlags);
                 break;
             case ACTION_POINTER_DOWN:
                 handleActionPointerDown(event, rawEvent, policyFlags);
@@ -843,6 +843,15 @@
         }
     }
 
+    private void handleActionDownStateTouchExploring(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        // This is an interrupted and continued touch exploration. Maintain the consistency of the
+        // event stream.
+        mSendTouchExplorationEndDelayed.cancel();
+        mSendTouchInteractionEndDelayed.cancel();
+        sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
+    }
+
     /**
      * Handles move events while touch exploring. this is also where we drag or delegate based on
      * the number of fingers moving on the screen.
@@ -1100,12 +1109,15 @@
     }
 
     /**
-     * Sends the enter events if needed. Such events are hover enter and touch explore
-     * gesture start.
+     * Sends the enter events if needed. Such events are hover enter and touch explore gesture
+     * start.
      *
      * @param policyFlags The policy flags associated with the event.
      */
     private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
+        if (!mState.isTouchExploring()) {
+            mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
+        }
         MotionEvent event = mState.getLastInjectedHoverEvent();
         if (event != null && event.getActionMasked() == ACTION_HOVER_EXIT) {
             final int pointerIdBits = event.getPointerIdBits();
@@ -1118,7 +1130,6 @@
         }
     }
 
-
     /**
      * Determines whether a two pointer gesture is a dragging one.
      *
diff --git a/services/core/Android.bp b/services/core/Android.bp
index c2774e5..a4d72b1 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -190,6 +190,7 @@
         "securebox",
         "apache-commons-math",
         "power_optimization_flags_lib",
+        "notification_flags_lib",
     ],
     javac_shard_size: 50,
     javacflags: [
@@ -239,4 +240,4 @@
 prebuilt_etc {
     name: "protolog.conf.json.gz",
     src: ":services.core.json.gz",
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a53c2fb..4dc76db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7409,6 +7409,9 @@
             case BugreportParams.BUGREPORT_MODE_WIFI:
                 type = "bugreportwifi";
                 break;
+            case BugreportParams.BUGREPORT_MODE_ONBOARDING:
+                type = "bugreportonboarding";
+                break;
             default:
                 throw new IllegalArgumentException(
                     "Provided bugreport type is not correct, value: "
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
index 4a10e8e..f78ca43 100644
--- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -103,8 +103,14 @@
      */
     @NonNull
     public OperationContext toAidlContext(@NonNull FingerprintAuthenticateOptions options) {
-        mAidlContext.authenticateReason = AuthenticateReason
-                .fingerprintAuthenticateReason(getAuthReason(options));
+        if (options.getVendorReason() != null) {
+            mAidlContext.authenticateReason = AuthenticateReason
+                    .vendorAuthenticateReason(options.getVendorReason());
+
+        } else {
+            mAidlContext.authenticateReason = AuthenticateReason
+                    .fingerprintAuthenticateReason(getAuthReason(options));
+        }
         mAidlContext.wakeReason = getWakeReason(options);
 
         return mAidlContext;
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 77213bf..b78f8a7 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -357,7 +357,7 @@
 
     static final int INVALID_PORT_ID = HdmiDeviceInfo.PORT_INVALID;
     static final int INVALID_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INVALID;
-    static final int PATH_INTERNAL = HdmiDeviceInfo.PATH_INTERNAL;
+    static final int TV_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INTERNAL;
 
     // The relationship from one path (physical address) to another.
     @IntDef({
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index dc416b2..824c8db 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -518,6 +518,18 @@
     @ServiceThreadOnly
     protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
         assertRunOnServiceThread();
+        // If the device is active source and received a <Routing Change> or <Routing Information>
+        // message to a physical address in the same active path do not change the Active Source
+        // status.
+        // E.g. TV [0.0.0.0] ------ Switch [2.0.0.0] ------ OTT [2.1.0.0] (Active Source)
+        // TV sends <Routing Change>[2.0.0.0] -> OTT is still Active Source
+        // TV sends <Routing Change>[0.0.0.0] -> OTT is not Active Source anymore.
+        // TV sends <Routing Change>[3.0.0.0] -> OTT is not Active Source anymore.
+        if (HdmiUtils.isInActiveRoutingPath(mService.getPhysicalAddress(), physicalAddress)
+                && physicalAddress != Constants.TV_PHYSICAL_ADDRESS
+                && isActiveSource()) {
+            return;
+        }
         if (physicalAddress != mService.getPhysicalAddress()) {
             setActiveSource(physicalAddress,
                     "HdmiCecLocalDevicePlayback#handleRoutingChangeAndInformation()");
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index e7a3db7..e96963b9 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -113,7 +113,7 @@
         }
         int param = tv().getActivePath();
         return param != Constants.INVALID_PHYSICAL_ADDRESS
-                ? param : Constants.PATH_INTERNAL;
+                ? param : Constants.TV_PHYSICAL_ADDRESS;
     }
 
     private void handleSendSystemAudioModeRequestTimeout() {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index e97a12a..6f9b7d6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -295,6 +295,8 @@
     private final PowerManager.WakeLock mDownloadPsdsWakeLock;
     @GuardedBy("mLock")
     private final Set<Integer> mPendingDownloadPsdsTypes = new HashSet<>();
+    @GuardedBy("mLock")
+    private final Set<Integer> mDownloadInProgressPsdsTypes = new HashSet<>();
 
     /**
      * Properties loaded from PROPERTIES_FILE.
@@ -767,8 +769,16 @@
             return;
         }
         synchronized (mLock) {
+            if (mDownloadInProgressPsdsTypes.contains(psdsType)) {
+                if (DEBUG) {
+                    Log.d(TAG,
+                            "PSDS type " + psdsType + " download in progress. Ignore the request.");
+                }
+                return;
+            }
             // hold wake lock while task runs
             mDownloadPsdsWakeLock.acquire(DOWNLOAD_PSDS_DATA_TIMEOUT_MS);
+            mDownloadInProgressPsdsTypes.add(psdsType);
         }
         Log.i(TAG, "WakeLock acquired by handleDownloadPsdsData()");
         Executors.newSingleThreadExecutor().execute(() -> {
@@ -818,6 +828,7 @@
                     Log.e(TAG, "WakeLock expired before release in "
                             + "handleDownloadPsdsData()");
                 }
+                mDownloadInProgressPsdsTypes.remove(psdsType);
             }
         });
     }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 63dc59c..37bcfbb 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1291,13 +1291,18 @@
         }
 
         long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
-        if (managerRecord.mLastSessionCreationRequest != null) {
+        SessionCreationRequest lastRequest = managerRecord.mLastSessionCreationRequest;
+        if (lastRequest != null) {
+            Slog.i(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "requestCreateSessionWithManagerLocked: Notifying failure for pending"
+                                + " session creation request - oldSession: %s, route: %s",
+                            lastRequest.mOldSession, lastRequest.mRoute));
             managerRecord.mUserRecord.mHandler.notifyRequestFailedToManager(
                     managerRecord.mManager,
-                    toOriginalRequestId(managerRecord.mLastSessionCreationRequest
-                            .mManagerRequestId),
+                    toOriginalRequestId(lastRequest.mManagerRequestId),
                     REASON_UNKNOWN_ERROR);
-            managerRecord.mLastSessionCreationRequest = null;
         }
         managerRecord.mLastSessionCreationRequest = new SessionCreationRequest(routerRecord,
                 MediaRoute2ProviderService.REQUEST_ID_NONE, uniqueRequestId,
diff --git a/services/core/java/com/android/server/notification/Android.bp b/services/core/java/com/android/server/notification/Android.bp
new file mode 100644
index 0000000..f26a25b
--- /dev/null
+++ b/services/core/java/com/android/server/notification/Android.bp
@@ -0,0 +1,12 @@
+java_aconfig_library {
+    name: "notification_flags_lib",
+    aconfig_declarations: "notification_flags",
+}
+
+aconfig_declarations {
+    name: "notification_flags",
+    package: "com.android.server.notification",
+    srcs: [
+        "flags.aconfig",
+    ],
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
new file mode 100644
index 0000000..c0bc6d1
--- /dev/null
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.notification"
+
+flag {
+  name: "expire_bitmaps"
+  namespace: "systemui"
+  description: "This flag controls removing expired notification bitmaps"
+  bug: "290381858"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 3ba307b..1134714 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -307,7 +307,8 @@
                 && mode != BugreportParams.BUGREPORT_MODE_REMOTE
                 && mode != BugreportParams.BUGREPORT_MODE_WEAR
                 && mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
-                && mode != BugreportParams.BUGREPORT_MODE_WIFI) {
+                && mode != BugreportParams.BUGREPORT_MODE_WIFI
+                && mode != BugreportParams.BUGREPORT_MODE_ONBOARDING) {
             Slog.w(TAG, "Unknown bugreport mode: " + mode);
             throw new IllegalArgumentException("Unknown bugreport mode: " + mode);
         }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 31869b8..3e18387 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3546,6 +3546,7 @@
                         + " no longer exists; its data will be wiped");
                 mInjector.getHandler().post(
                         () -> mRemovePackageHelper.removePackageData(ps, userIds, null, 0, false));
+                expectingBetter.put(ps.getPackageName(), ps.getPath());
             } else {
                 // we still have a disabled system package, but, it still might have
                 // been removed. check the code path still exists and check there's
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 1e6486a..4a4214f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -308,10 +308,8 @@
     private final ServiceThread mHandlerThread;
     private final Handler mHandler;
     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
-    private final BatterySaverController mBatterySaverController;
-    private final BatterySaverPolicy mBatterySaverPolicy;
+    @Nullable
     private final BatterySaverStateMachine mBatterySaverStateMachine;
-    private final BatterySavingStats mBatterySavingStats;
     private final LowPowerStandbyController mLowPowerStandbyController;
     private final AttentionDetector mAttentionDetector;
     private final FaceDownDetector mFaceDownDetector;
@@ -325,6 +323,8 @@
     private final PermissionCheckerWrapper mPermissionCheckerWrapper;
     private final PowerPropertiesWrapper mPowerPropertiesWrapper;
     private final DeviceConfigParameterProvider mDeviceConfigProvider;
+    // True if battery saver is supported on this device.
+    private final boolean mBatterySaverSupported;
 
     private boolean mDisableScreenWakeLocksWhileCached;
 
@@ -968,20 +968,13 @@
             return suspendBlocker;
         }
 
-        BatterySaverPolicy createBatterySaverPolicy(
-                Object lock, Context context, BatterySavingStats batterySavingStats) {
-            return new BatterySaverPolicy(lock, context, batterySavingStats);
-        }
-
-        BatterySaverController createBatterySaverController(
-                Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
-                BatterySavingStats batterySavingStats) {
-            return new BatterySaverController(lock, context, BackgroundThread.get().getLooper(),
+        BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) {
+            BatterySavingStats batterySavingStats = new BatterySavingStats(lock);
+            BatterySaverPolicy batterySaverPolicy = new BatterySaverPolicy(lock, context,
+                    batterySavingStats);
+            BatterySaverController batterySaverController = new BatterySaverController(lock,
+                    context, BackgroundThread.get().getLooper(),
                     batterySaverPolicy, batterySavingStats);
-        }
-
-        BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
-                BatterySaverController batterySaverController) {
             return new BatterySaverStateMachine(lock, context, batterySaverController);
         }
 
@@ -1155,13 +1148,11 @@
         mFaceDownDetector = new FaceDownDetector(this::onFlip);
         mScreenUndimDetector = new ScreenUndimDetector();
 
-        mBatterySavingStats = new BatterySavingStats(mLock);
-        mBatterySaverPolicy =
-                mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
-        mBatterySaverController = mInjector.createBatterySaverController(mLock, mContext,
-                mBatterySaverPolicy, mBatterySavingStats);
-        mBatterySaverStateMachine = mInjector.createBatterySaverStateMachine(mLock, mContext,
-                mBatterySaverController);
+        mBatterySaverSupported = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_batterySaverSupported);
+        mBatterySaverStateMachine =
+                mBatterySaverSupported ? mInjector.createBatterySaverStateMachine(mLock, mContext)
+                        : null;
 
         mLowPowerStandbyController = mInjector.createLowPowerStandbyController(mContext,
                 Looper.getMainLooper());
@@ -1300,7 +1291,9 @@
                 mBootCompleted = true;
                 mDirty |= DIRTY_BOOT_COMPLETED;
 
-                mBatterySaverStateMachine.onBootCompleted();
+                if (mBatterySaverSupported) {
+                    mBatterySaverStateMachine.onBootCompleted();
+                }
                 userActivityNoUpdateLocked(
                         now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
 
@@ -1390,8 +1383,9 @@
         final ContentResolver resolver = mContext.getContentResolver();
         mConstants.start(resolver);
 
-        mBatterySaverController.systemReady();
-        mBatterySaverPolicy.systemReady();
+        if (mBatterySaverSupported) {
+            mBatterySaverStateMachine.systemReady();
+        }
         mFaceDownDetector.systemReady(mContext);
         mScreenUndimDetector.systemReady(mContext);
 
@@ -2601,7 +2595,10 @@
                 }
             }
 
-            mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow);
+            if (mBatterySaverSupported) {
+                mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel,
+                        mBatteryLevelLow);
+            }
         }
     }
 
@@ -3554,7 +3551,11 @@
                         mDozeScreenStateOverrideFromDreamManager,
                         mDozeScreenBrightnessOverrideFromDreamManagerFloat,
                         mDrawWakeLockOverrideFromSidekick,
-                        mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS),
+                        mBatterySaverSupported
+                                ?
+                                mBatterySaverStateMachine.getBatterySaverPolicy()
+                                        .getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS)
+                                : new PowerSaveState.Builder().build(),
                         sQuiescent, mDozeAfterScreenOff, mBootCompleted,
                         mScreenBrightnessBoostInProgress, mRequestWaitForNegativeProximity);
                 int wakefulness = powerGroup.getWakefulnessLocked();
@@ -3884,7 +3885,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "setLowPowerModeInternal " + enabled + " mIsPowered=" + mIsPowered);
             }
-            if (mIsPowered) {
+            if (mIsPowered || !mBatterySaverSupported) {
                 return false;
             }
 
@@ -4378,7 +4379,8 @@
 
     private boolean setPowerModeInternal(int mode, boolean enabled) {
         // Maybe filter the event.
-        if (mode == Mode.LAUNCH && enabled && mBatterySaverController.isLaunchBoostDisabled()) {
+        if (mBatterySaverStateMachine == null || (mode == Mode.LAUNCH && enabled
+                && mBatterySaverStateMachine.getBatterySaverController().isLaunchBoostDisabled())) {
             return false;
         }
         return mNativeWrapper.nativeSetPowerMode(mode, enabled);
@@ -4718,8 +4720,12 @@
             pw.println();
             pw.println("Display Power: " + mDisplayPowerCallbacks);
 
-            mBatterySaverPolicy.dump(pw);
-            mBatterySaverStateMachine.dump(pw);
+            if (mBatterySaverSupported) {
+                mBatterySaverStateMachine.getBatterySaverPolicy().dump(pw);
+                mBatterySaverStateMachine.dump(pw);
+            } else {
+                pw.println("Battery Saver: DISABLED");
+            }
             mAttentionDetector.dump(pw);
 
             pw.println();
@@ -5101,8 +5107,10 @@
                 proto.end(uIDToken);
             }
 
-            mBatterySaverStateMachine.dumpProto(proto,
-                    PowerManagerServiceDumpProto.BATTERY_SAVER_STATE_MACHINE);
+            if (mBatterySaverSupported) {
+                mBatterySaverStateMachine.dumpProto(proto,
+                        PowerManagerServiceDumpProto.BATTERY_SAVER_STATE_MACHINE);
+            }
 
             mHandler.getLooper().dumpDebug(proto, PowerManagerServiceDumpProto.LOOPER);
 
@@ -5986,7 +5994,8 @@
         public boolean isPowerSaveMode() {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mBatterySaverController.isEnabled();
+                return mBatterySaverSupported
+                        && mBatterySaverStateMachine.getBatterySaverController().isEnabled();
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -5996,7 +6005,12 @@
         public PowerSaveState getPowerSaveState(@ServiceType int serviceType) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mBatterySaverPolicy.getBatterySaverPolicy(serviceType);
+                // Return default PowerSaveState if battery saver is not supported.
+                return mBatterySaverSupported
+                        ?
+                        mBatterySaverStateMachine.getBatterySaverPolicy().getBatterySaverPolicy(
+                                serviceType)
+                        : new PowerSaveState.Builder().build();
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -6017,11 +6031,24 @@
             }
         }
 
-        @Override // Binder call
-        public BatterySaverPolicyConfig getFullPowerSavePolicy() {
+        @Override
+        public boolean isBatterySaverSupported() {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mBatterySaverStateMachine.getFullBatterySaverPolicy();
+                return mBatterySaverSupported;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
+        public BatterySaverPolicyConfig getFullPowerSavePolicy() {
+            // Return default BatterySaverPolicyConfig if battery saver is not supported.
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return mBatterySaverSupported
+                        ? mBatterySaverStateMachine.getFullBatterySaverPolicy()
+                        : new BatterySaverPolicyConfig.Builder().build();
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -6036,7 +6063,8 @@
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mBatterySaverStateMachine.setFullBatterySaverPolicy(config);
+                return mBatterySaverSupported
+                        && mBatterySaverStateMachine.setFullBatterySaverPolicy(config);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -6073,7 +6101,8 @@
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mBatterySaverStateMachine.setAdaptiveBatterySaverPolicy(config);
+                return mBatterySaverSupported
+                        && mBatterySaverStateMachine.setAdaptiveBatterySaverPolicy(config);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -6088,7 +6117,8 @@
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mBatterySaverStateMachine.setAdaptiveBatterySaverEnabled(enabled);
+                return mBatterySaverSupported
+                        && mBatterySaverStateMachine.setAdaptiveBatterySaverEnabled(enabled);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -6953,12 +6983,21 @@
 
         @Override
         public PowerSaveState getLowPowerState(@ServiceType int serviceType) {
-            return mBatterySaverPolicy.getBatterySaverPolicy(serviceType);
+            // Return default PowerSaveState if battery saver is not supported.
+            return mBatterySaverSupported
+                    ?
+                    mBatterySaverStateMachine.getBatterySaverPolicy().getBatterySaverPolicy(
+                            serviceType) : new PowerSaveState.Builder().build();
         }
 
         @Override
         public void registerLowPowerModeObserver(LowPowerModeListener listener) {
-            mBatterySaverController.addListener(listener);
+            if (mBatterySaverSupported) {
+                mBatterySaverStateMachine.getBatterySaverController().addListener(listener);
+            } else {
+                Slog.w(TAG,
+                        "Battery saver is not supported, no low power mode observer registered");
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 57d69c2..8c1e9a5 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -40,7 +40,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
-import com.android.server.power.PowerManagerService;
 import com.android.server.power.batterysaver.BatterySaverPolicy.BatterySaverPolicyListener;
 import com.android.server.power.batterysaver.BatterySaverPolicy.Policy;
 import com.android.server.power.batterysaver.BatterySaverPolicy.PolicyLevel;
@@ -223,7 +222,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService} on system ready, *with no lock held*.
+     * Called by {@link BatterySaverStateMachine} on system ready, *with no lock held*.
      */
     public void systemReady() {
         final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 07d1844..e3f3638 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -41,7 +41,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ConcurrentUtils;
-import com.android.server.power.PowerManagerService;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -283,7 +282,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService#onBootPhase}, *with no lock held.*
+     * Called by {@link BatterySaverStateMachine#systemReady()}, *with no lock held.*
      */
     public void systemReady() {
         ConcurrentUtils.wtfIfLockHeld(TAG, mLock);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index e0bbd36..b22e37b 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -44,6 +44,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
 import com.android.server.power.BatterySaverStateMachineProto;
+import com.android.server.power.PowerManagerService;
 
 import java.io.PrintWriter;
 import java.time.Duration;
@@ -253,6 +254,24 @@
                 com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold);
     }
 
+    /**
+     * Called by {@link PowerManagerService} on system ready, *with no lock held*.
+     */
+    public void systemReady() {
+        mBatterySaverController.systemReady();
+        getBatterySaverPolicy().systemReady();
+    }
+
+    /** @return Battery saver controller. */
+    public BatterySaverController getBatterySaverController() {
+        return mBatterySaverController;
+    }
+
+    /** @return Battery saver policy. */
+    public BatterySaverPolicy getBatterySaverPolicy() {
+        return mBatterySaverController.getBatterySaverPolicy();
+    }
+
     /** @return true if the automatic percentage based mode should be used */
     private boolean isAutomaticModeActiveLocked() {
         return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE
@@ -937,8 +956,7 @@
             ipw.print(mBatterySaverController.isAdaptiveEnabled());
             if (mBatterySaverController.isAdaptiveEnabled()) {
                 ipw.print(" (advertise=");
-                ipw.print(
-                        mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
+                ipw.print(getBatterySaverPolicy().shouldAdvertiseIsEnabled());
                 ipw.print(")");
             }
             ipw.decreaseIndent();
@@ -1005,7 +1023,7 @@
             proto.write(BatterySaverStateMachineProto.IS_ADAPTIVE_ENABLED,
                     mBatterySaverController.isAdaptiveEnabled());
             proto.write(BatterySaverStateMachineProto.SHOULD_ADVERTISE_IS_ENABLED,
-                    mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
+                    getBatterySaverPolicy().shouldAdvertiseIsEnabled());
 
             proto.write(BatterySaverStateMachineProto.BOOT_COMPLETED, mBootCompleted);
             proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 261d6bc..03efb1b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4648,7 +4648,14 @@
     void reportSystemGestureExclusionChanged(Session session, IWindow window,
             List<Rect> exclusionRects) {
         synchronized (mGlobalLock) {
-            final WindowState win = windowForClientLocked(session, window, true);
+            final WindowState win = windowForClientLocked(session, window,
+                    false /* throwOnError */);
+            if (win == null) {
+                Slog.i(TAG_WM,
+                        "reportSystemGestureExclusionChanged(): No window state for package:"
+                                + session.mPackageName);
+                return;
+            }
             if (win.setSystemGestureExclusion(exclusionRects)) {
                 win.getDisplayContent().updateSystemGestureExclusion();
             }
@@ -4658,7 +4665,14 @@
     void reportKeepClearAreasChanged(Session session, IWindow window,
             List<Rect> restricted, List<Rect> unrestricted) {
         synchronized (mGlobalLock) {
-            final WindowState win = windowForClientLocked(session, window, true);
+            final WindowState win = windowForClientLocked(session, window,
+                    false /* throwOnError */);
+            if (win == null) {
+                Slog.i(TAG_WM,
+                        "reportKeepClearAreasChanged(): No window state for package:"
+                                + session.mPackageName);
+                return;
+            }
             if (win.setKeepClearAreas(restricted, unrestricted)) {
                 win.getDisplayContent().updateKeepClearAreas();
             }
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index ad098b7..4cd018b 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -144,12 +144,20 @@
             }
             uinput_abs_setup slotAbsSetup;
             slotAbsSetup.code = ABS_MT_SLOT;
-            slotAbsSetup.absinfo.maximum = MAX_POINTERS;
+            slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
             slotAbsSetup.absinfo.minimum = 0;
             if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
                 ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
                 return invalidFd();
             }
+            uinput_abs_setup trackingIdAbsSetup;
+            trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
+            trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
+            trackingIdAbsSetup.absinfo.minimum = 0;
+            if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
+                ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
+                return invalidFd();
+            }
         }
         if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
             ALOGE("Error creating uinput device: %s", strerror(errno));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 522ee34..e7855bc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -79,14 +79,7 @@
 
     @Override
     public boolean isScreenCaptureAllowed(int userHandle) {
-        if (DevicePolicyManagerService.isPolicyEngineForFinanceFlagEnabled()) {
-            return isScreenCaptureAllowedInPolicyEngine(userHandle);
-        } else {
-            synchronized (mLock) {
-                return mScreenCaptureDisallowedUser != UserHandle.USER_ALL
-                        && mScreenCaptureDisallowedUser != userHandle;
-            }
-        }
+        return isScreenCaptureAllowedInPolicyEngine(userHandle);
     }
 
     private boolean isScreenCaptureAllowedInPolicyEngine(int userHandle) {
@@ -182,11 +175,7 @@
         synchronized (mLock) {
             pw.println("Device policy cache:");
             pw.increaseIndent();
-            if (DevicePolicyManagerService.isPolicyEngineForFinanceFlagEnabled()) {
-                pw.println("Screen capture disallowed users: " + mScreenCaptureDisallowedUsers);
-            } else {
-                pw.println("Screen capture disallowed user: " + mScreenCaptureDisallowedUser);
-            }
+            pw.println("Screen capture disallowed users: " + mScreenCaptureDisallowedUsers);
             pw.println("Password quality: " + mPasswordQuality);
             pw.println("Permission policy: " + mPermissionPolicy);
             pw.println("Admin can grant sensors permission: " + mCanGrantSensorsPermissions.get());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c323a7f..b3fa782 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -241,6 +241,7 @@
 import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
 import static android.provider.Telephony.Carriers.INVALID_APN_ID;
 import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
+
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -1190,9 +1191,7 @@
                         maybeResumeDeviceWideLoggingLocked();
                     }
                 }
-                if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
-                    mDevicePolicyEngine.handleUserRemoved(userHandle);
-                }
+                mDevicePolicyEngine.handleUserRemoved(userHandle);
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
                 sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle);
                 synchronized (getLockObject()) {
@@ -1442,10 +1441,7 @@
                     && (owner.getPackageName().equals(packageName))) {
                 startOwnerService(userHandle, "package-broadcast");
             }
-            if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
-                mDevicePolicyEngine.handlePackageChanged(
-                        packageName, userHandle, removedAdminPackage);
-            }
+            mDevicePolicyEngine.handlePackageChanged(packageName, userHandle, removedAdminPackage);
             // Persist updates if the removed package was an admin or delegate.
             if (removedAdmin || removedDelegate) {
                 saveSettingsLocked(policy.mUserId);
@@ -1453,7 +1449,6 @@
         }
         if (removedAdmin) {
             // The removed admin might have disabled camera, so update user restrictions.
-            pushUserRestrictions(userHandle);
             pushMeteredDisabledPackages(userHandle);
         }
     }
@@ -2144,9 +2139,7 @@
         mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
 
         mDeviceManagementResourcesProvider.load();
-        if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
-            mDevicePolicyEngine.load();
-        }
+        mDevicePolicyEngine.load();
 
         mContactSystemRoleHolders = fetchOemSystemHolders(/* roleResIds...= */
                 com.android.internal.R.string.config_defaultSms,
@@ -2278,11 +2271,9 @@
                 if (parentAdmin != null) {
                     parentAdmin.userRestrictions = null;
                 }
-                pushUserRestrictions(userHandle);
             }
             mOwners.removeProfileOwner(userHandle);
             mOwners.writeProfileOwner(userHandle);
-            pushScreenCapturePolicy(userHandle);
 
             DevicePolicyData policy = mUserData.get(userHandle);
             if (policy != null) {
@@ -2640,20 +2631,14 @@
             ActiveAdmin profileOwner, boolean newOwner) {
         if (newOwner || mInjector.settingsSecureGetIntForUser(
                 Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) != 0) {
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                mDevicePolicyEngine.setLocalPolicy(
-                        PolicyDefinition.getPolicyDefinitionForUserRestriction(
-                                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
-                        EnforcingAdmin.createEnterpriseEnforcingAdmin(
-                                profileOwner.info.getComponent(),
-                                profileOwner.getUserHandle().getIdentifier()),
-                        new BooleanPolicyValue(true),
-                        userId);
-            } else {
-                profileOwner.ensureUserRestrictions().putBoolean(
-                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
-                saveUserRestrictionsLocked(userId);
-            }
+            mDevicePolicyEngine.setLocalPolicy(
+                    PolicyDefinition.getPolicyDefinitionForUserRestriction(
+                            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
+                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                            profileOwner.info.getComponent(),
+                            profileOwner.getUserHandle().getIdentifier()),
+                    new BooleanPolicyValue(true),
+                    userId);
             mInjector.settingsSecurePutIntForUser(
                     Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId);
         }
@@ -2668,41 +2653,18 @@
         if (defaultRestrictions.equals(admin.defaultEnabledRestrictionsAlreadySet)) {
             return; // The same set of default restrictions has been already applied.
         }
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            for (String restriction : defaultRestrictions) {
-                mDevicePolicyEngine.setLocalPolicy(
-                        PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction),
-                        EnforcingAdmin.createEnterpriseEnforcingAdmin(
-                                admin.info.getComponent(),
-                                admin.getUserHandle().getIdentifier()),
-                        new BooleanPolicyValue(true),
-                        userId);
-            }
-            admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions);
-            Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " +
-                    defaultRestrictions);
-            return;
+        for (String restriction : defaultRestrictions) {
+            mDevicePolicyEngine.setLocalPolicy(
+                    PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction),
+                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                            admin.info.getComponent(),
+                            admin.getUserHandle().getIdentifier()),
+                    new BooleanPolicyValue(true),
+                    userId);
         }
-
-        Slogf.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId);
-
-        if (VERBOSE_LOG) {
-            Slogf.d(LOG_TAG, "Default enabled restrictions: "
-                    + defaultRestrictions
-                    + ". Restrictions already enabled: "
-                    + admin.defaultEnabledRestrictionsAlreadySet);
-        }
-
-        final Set<String> restrictionsToSet = new ArraySet<>(defaultRestrictions);
-        restrictionsToSet.removeAll(admin.defaultEnabledRestrictionsAlreadySet);
-        if (!restrictionsToSet.isEmpty()) {
-            for (final String restriction : restrictionsToSet) {
-                admin.ensureUserRestrictions().putBoolean(restriction, true);
-            }
-            admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
-            Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
-            saveUserRestrictionsLocked(userId);
-        }
+        admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions);
+        Slogf.i(LOG_TAG, "Enabled the following restrictions by default: "
+                + defaultRestrictions);
     }
 
     private void setDeviceOwnershipSystemPropertyLocked() {
@@ -2765,7 +2727,6 @@
                     // Apply user restriction to parent active admin instead
                     parent.ensureUserRestrictions().putBoolean(
                             UserManager.DISALLOW_CONFIG_DATE_TIME, true);
-                    pushUserRestrictions(userId);
                 }
             }
         }
@@ -3297,10 +3258,6 @@
 
         policy.validatePasswordOwner();
         updateMaximumTimeToLockLocked(userHandle);
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userHandle);
-            updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
-        }
         if (policy.mStatusBarDisabled) {
             setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
         }
@@ -3470,9 +3427,6 @@
             }
 
             revertTransferOwnershipIfNecessaryLocked();
-            if (!isPolicyEngineForFinanceFlagEnabled()) {
-                updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
-            }
         }
 
         // In case flag value has changed, we apply it during boot to avoid doing it concurrently
@@ -3545,9 +3499,6 @@
             deleteTransferOwnershipBundleLocked(metadata.userId);
         }
         updateSystemUpdateFreezePeriodsRecord(/* saveIfChanged */ true);
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            pushUserControlDisabledPackagesLocked(metadata.userId);
-        }
     }
 
     private void maybeLogStart() {
@@ -3584,13 +3535,6 @@
     }
 
     void handleStartUser(int userId) {
-        synchronized (getLockObject()) {
-            pushScreenCapturePolicy(userId);
-            if (!isPolicyEngineForFinanceFlagEnabled()) {
-                pushUserControlDisabledPackagesLocked(userId);
-            }
-        }
-        pushUserRestrictions(userId);
         // When system user is started (device boot), load cache for all users.
         // This is to mitigate the potential race between loading the cache and keyguard
         // reading the value during user switch, due to onStartUser() being asynchronous.
@@ -3615,9 +3559,7 @@
         }
 
         startOwnerService(userId, "start-user");
-        if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
-            mDevicePolicyEngine.handleStartUser(userId);
-        }
+        mDevicePolicyEngine.handleStartUser(userId);
     }
 
     void pushUserControlDisabledPackagesLocked(int userId) {
@@ -3642,9 +3584,7 @@
 
     void handleUnlockUser(int userId) {
         startOwnerService(userId, "unlock-user");
-        if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
-            mDevicePolicyEngine.handleUnlockUser(userId);
-        }
+        mDevicePolicyEngine.handleUnlockUser(userId);
     }
 
     void handleOnUserUnlocked(int userId) {
@@ -3654,9 +3594,7 @@
     void handleStopUser(int userId) {
         updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
         mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
-        if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
-            mDevicePolicyEngine.handleStopUser(userId);
-        }
+        mDevicePolicyEngine.handleStopUser(userId);
     }
 
     private void startOwnerService(int userId, String actionForLog) {
@@ -3690,9 +3628,7 @@
         }
         for (Integer userId : deletedUsers) {
             removeUserData(userId);
-            if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
-                mDevicePolicyEngine.handleUserRemoved(userId);
-            }
+            mDevicePolicyEngine.handleUserRemoved(userId);
         }
     }
 
@@ -3879,16 +3815,14 @@
         final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver);
         final int oldAdminUid = adminToTransfer.getUid();
 
-        if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
-            EnforcingAdmin oldAdmin =
-                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
-                            outgoingReceiver, userHandle, adminToTransfer);
-            EnforcingAdmin newAdmin =
-                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
-                            incomingReceiver, userHandle, adminToTransfer);
+        EnforcingAdmin oldAdmin =
+                EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                        outgoingReceiver, userHandle, adminToTransfer);
+        EnforcingAdmin newAdmin =
+                EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                        incomingReceiver, userHandle, adminToTransfer);
 
-            mDevicePolicyEngine.transferPolicies(oldAdmin, newAdmin);
-        }
+        mDevicePolicyEngine.transferPolicies(oldAdmin, newAdmin);
 
         adminToTransfer.transfer(incomingDeviceInfo);
         policy.mAdminMap.remove(outgoingReceiver);
@@ -4194,11 +4128,9 @@
             mInjector.binderWithCleanCallingIdentity(() ->
                     removeActiveAdminLocked(adminReceiver, userHandle));
         }
-        if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
-            mDevicePolicyEngine.removePoliciesForAdmin(
-                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
-                            adminReceiver, userHandle, admin));
-        }
+        mDevicePolicyEngine.removePoliciesForAdmin(
+                EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                        adminReceiver, userHandle, admin));
     }
 
     private boolean canSetPasswordQualityOnParent(String packageName, final CallerIdentity caller) {
@@ -7558,47 +7490,17 @@
         if (!mHasFeature && !hasCallingOrSelfPermission(permission.MASTER_CLEAR)) {
             return;
         }
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(callerPackageName);
-        }  else {
-            caller = getCallerIdentity();
-        }
-        ActiveAdmin admin;
+        CallerIdentity caller = getCallerIdentity(callerPackageName);
 
         boolean calledByProfileOwnerOnOrgOwnedDevice =
                 isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId());
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
-                    /*admin=*/ null,
-                    /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, MASTER_CLEAR},
-                    USES_POLICY_WIPE_DATA,
-                    caller.getPackageName(),
-                    factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance));
-            admin = enforcingAdmin.getActiveAdmin();
-        } else {
-            if (calledOnParentInstance) {
-                Preconditions.checkCallAuthorization(calledByProfileOwnerOnOrgOwnedDevice,
-                        "Wiping the entire device can only be done by a profile owner on "
-                                + "organization-owned device.");
-            }
-            if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
-                Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
-                                || calledByProfileOwnerOnOrgOwnedDevice
-                                || isFinancedDeviceOwner(caller),
-                        "Only device owners or profile owners of organization-owned device can set "
-                                + "WIPE_RESET_PROTECTION_DATA");
-            }
-            synchronized (getLockObject()) {
-                admin = getActiveAdminWithPolicyForUidLocked(/* who= */ null,
-                        DeviceAdminInfo.USES_POLICY_WIPE_DATA, caller.getUid());
-            }
-            Preconditions.checkCallAuthorization(
-                    (admin != null) || hasCallingOrSelfPermission(permission.MASTER_CLEAR),
-                    "No active admin for user %d and caller %d does not hold MASTER_CLEAR "
-                            + "permission",
-                    caller.getUserId(), caller.getUid());
-        }
+        EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
+                /*admin=*/ null,
+                /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, MASTER_CLEAR},
+                USES_POLICY_WIPE_DATA,
+                caller.getPackageName(),
+                factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance));
+        ActiveAdmin admin = enforcingAdmin.getActiveAdmin();
 
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_WIPE_DATA);
 
@@ -8639,62 +8541,36 @@
             return;
         }
 
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackage);
-        } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            caller = getCallerIdentity(who);
-            if (parent) {
-                Preconditions.checkCallAuthorization(
-                        isProfileOwnerOfOrganizationOwnedDevice(caller));
+        CallerIdentity caller = getCallerIdentity(who, callerPackage);
+        int callerUserId = Binder.getCallingUserHandle().getIdentifier();
+        int targetUserId = parent ? getProfileParentId(callerUserId) : callerUserId;
+        EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+                who, MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, caller.getPackageName(),
+                targetUserId);
+        if ((parent && isProfileOwnerOfOrganizationOwnedDevice(caller))
+                || isDefaultDeviceOwner(caller)) {
+            if (disabled) {
+                mDevicePolicyEngine.setGlobalPolicy(
+                        PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+                        admin,
+                        new BooleanPolicyValue(disabled));
             } else {
-                Preconditions.checkCallAuthorization(isProfileOwner(caller)
-                        || isDefaultDeviceOwner(caller));
-            }
-        }
-
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            int callerUserId = Binder.getCallingUserHandle().getIdentifier();
-            int targetUserId = parent ? getProfileParentId(callerUserId) : callerUserId;
-            EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
-                    who, MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, caller.getPackageName(),
-                    targetUserId);
-            if ((parent && isProfileOwnerOfOrganizationOwnedDevice(caller))
-                    || isDefaultDeviceOwner(caller)) {
-                if (disabled) {
-                    mDevicePolicyEngine.setGlobalPolicy(
-                            PolicyDefinition.SCREEN_CAPTURE_DISABLED,
-                            admin,
-                            new BooleanPolicyValue(disabled));
-                } else {
-                    mDevicePolicyEngine.removeGlobalPolicy(
-                            PolicyDefinition.SCREEN_CAPTURE_DISABLED,
-                            admin);
-                }
-            } else {
-                if (disabled) {
-                    mDevicePolicyEngine.setLocalPolicy(
-                            PolicyDefinition.SCREEN_CAPTURE_DISABLED,
-                            admin,
-                            new BooleanPolicyValue(disabled),
-                            callerUserId);
-                } else {
-                    mDevicePolicyEngine.removeLocalPolicy(
-                            PolicyDefinition.SCREEN_CAPTURE_DISABLED,
-                            admin,
-                            callerUserId);
-                }
+                mDevicePolicyEngine.removeGlobalPolicy(
+                        PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+                        admin);
             }
         } else {
-            synchronized (getLockObject()) {
-                ActiveAdmin ap = getParentOfAdminIfRequired(
-                        getProfileOwnerOrDefaultDeviceOwnerLocked(caller.getUserId()), parent);
-                if (ap.disableScreenCapture != disabled) {
-                    ap.disableScreenCapture = disabled;
-                    saveSettingsLocked(caller.getUserId());
-                    pushScreenCapturePolicy(caller.getUserId());
-                }
+            if (disabled) {
+                mDevicePolicyEngine.setLocalPolicy(
+                        PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+                        admin,
+                        new BooleanPolicyValue(disabled),
+                        callerUserId);
+            } else {
+                mDevicePolicyEngine.removeLocalPolicy(
+                        PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+                        admin,
+                        callerUserId);
             }
         }
         DevicePolicyEventLogger
@@ -8704,42 +8580,6 @@
                 .write();
     }
 
-    // Push the screen capture policy for a given userId. If screen capture is disabled by the
-    // DO or COPE PO on the parent profile, then this takes precedence as screen capture will
-    // be disabled device-wide.
-    private void pushScreenCapturePolicy(int adminUserId) {
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            return;
-        }
-        // Update screen capture device-wide if disabled by the DO or COPE PO on the parent profile.
-        // TODO(b/261999445): remove
-        ActiveAdmin admin;
-        if (isHeadlessFlagEnabled()) {
-            admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(
-                    mUserManagerInternal.getProfileParentId(adminUserId));
-        } else {
-            admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(
-                    UserHandle.USER_SYSTEM);
-        }
-        if (admin != null && admin.disableScreenCapture) {
-            setScreenCaptureDisabled(UserHandle.USER_ALL);
-            return;
-        }
-        // Otherwise, update screen capture only for the calling user.
-        admin = getProfileOwnerAdminLocked(adminUserId);
-        if (admin != null && admin.disableScreenCapture) {
-            setScreenCaptureDisabled(adminUserId);
-            return;
-        }
-        // If the admin is permission based, update only for the calling user.
-        admin = getUserData(adminUserId).createOrGetPermissionBasedAdmin(adminUserId);
-        if (admin != null && admin.disableScreenCapture) {
-            setScreenCaptureDisabled(adminUserId);
-            return;
-        }
-        setScreenCaptureDisabled(UserHandle.USER_NULL);
-    }
-
     // Set the latest screen capture policy, overriding any existing ones.
     // userHandle can be one of USER_ALL, USER_NULL or a concrete userId.
     private void setScreenCaptureDisabled(int userHandle) {
@@ -8766,14 +8606,10 @@
             Preconditions.checkCallAuthorization(
                     isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
         }
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            Boolean disallowed = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.SCREEN_CAPTURE_DISABLED,
-                    userHandle);
-            return disallowed != null && disallowed;
-        } else {
-            return !mPolicyCache.isScreenCaptureAllowed(userHandle);
-        }
+        Boolean disallowed = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.SCREEN_CAPTURE_DISABLED,
+                userHandle);
+        return disallowed != null && disallowed;
     }
 
     private void updateScreenCaptureDisabled() {
@@ -8882,23 +8718,9 @@
             Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
                     "Managed profile cannot set auto time required");
 
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                EnforcingAdmin admin = getEnforcingAdminForCaller(who, who.getPackageName());
-                setGlobalUserRestrictionInternal(
-                        admin, UserManager.DISALLOW_CONFIG_DATE_TIME, required);
-            } else {
-                ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
-                if (admin.requireAutoTime != required) {
-                    admin.requireAutoTime = required;
-                    saveSettingsLocked(caller.getUserId());
-                    requireAutoTimeChanged = true;
-                }
-                // requireAutoTime is now backed by DISALLOW_CONFIG_DATE_TIME restriction, so
-                // propagate updated restrictions to the framework.
-                if (requireAutoTimeChanged) {
-                    pushUserRestrictions(caller.getUserId());
-                }
-            }
+            EnforcingAdmin admin = getEnforcingAdminForCaller(who, who.getPackageName());
+            setGlobalUserRestrictionInternal(
+                    admin, UserManager.DISALLOW_CONFIG_DATE_TIME, required);
         }
         // Turn AUTO_TIME on in settings if it is required
         if (required) {
@@ -8921,31 +8743,11 @@
         if (!mHasFeature) {
             return false;
         }
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            Boolean required = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.getPolicyDefinitionForUserRestriction(
-                            UserManager.DISALLOW_CONFIG_DATE_TIME),
-                    mInjector.binderGetCallingUserHandle().getIdentifier());
-            return required != null && required;
-        } else {
-            synchronized (getLockObject()) {
-                ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
-                if (deviceOwner != null && deviceOwner.requireAutoTime) {
-                    // If the device owner enforces auto time, we don't need to check the PO's
-                    return true;
-                }
-
-                // Now check to see if any profile owner on any user enforces auto time
-                for (Integer userId : mOwners.getProfileOwnerKeys()) {
-                    ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
-                    if (profileOwner != null && profileOwner.requireAutoTime) {
-                        return true;
-                    }
-                }
-
-                return false;
-            }
-        }
+        Boolean required = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.getPolicyDefinitionForUserRestriction(
+                        UserManager.DISALLOW_CONFIG_DATE_TIME),
+                mInjector.binderGetCallingUserHandle().getIdentifier());
+        return required != null && required;
     }
 
     /**
@@ -9240,47 +9042,23 @@
             return;
         }
 
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         final int userId = caller.getUserId();
 
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_CAMERA_DISABLED);
-
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                    who,
-                    MANAGE_DEVICE_POLICY_CAMERA,
-                    caller.getPackageName(),
-                    getProfileParentUserIfRequested(userId, parent));
-            try {
-                setBackwardCompatibleUserRestriction(
-                        caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent);
-            } catch (IllegalStateException e) {
-                throw new IllegalStateException(
-                        "Please use addUserRestriction or addUserRestrictionGlobally using the key"
-                                + " UserManager.DISALLOW_CAMERA to disable the camera locally or"
-                                + " globally, respectively");
-            }
-        } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            if (parent) {
-                Preconditions.checkCallAuthorization(
-                        isProfileOwnerOfOrganizationOwnedDevice(caller));
-            }
-            synchronized (getLockObject()) {
-                ActiveAdmin admin = getActiveAdminForCallerLocked(who,
-                        DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent);
-                if (admin.disableCamera != disabled) {
-                    admin.disableCamera = disabled;
-                    saveSettingsLocked(userId);
-                }
-            }
-            // Tell the user manager that the restrictions have changed.
-            pushUserRestrictions(userId);
+        EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                who,
+                MANAGE_DEVICE_POLICY_CAMERA,
+                caller.getPackageName(),
+                getProfileParentUserIfRequested(userId, parent));
+        try {
+            setBackwardCompatibleUserRestriction(
+                    caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent);
+        } catch (IllegalStateException e) {
+            throw new IllegalStateException(
+                    "Please use addUserRestriction or addUserRestrictionGlobally using the key"
+                            + " UserManager.DISALLOW_CAMERA to disable the camera locally or"
+                            + " globally, respectively");
         }
 
         final int affectedUserId = parent ? getProfileParentId(userId) : userId;
@@ -9306,66 +9084,26 @@
         if (!mHasFeature) {
             return false;
         }
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            Preconditions.checkCallAuthorization(
-                    hasFullCrossUsersPermission(caller, userHandle)
-                            || isCameraServerUid(caller)
-                            || hasPermission(MANAGE_DEVICE_POLICY_CAMERA,
-                                caller.getPackageName(), userHandle)
-                            || hasPermission(QUERY_ADMIN_POLICY, caller.getPackageName()));
-        } else {
-            Preconditions.checkCallAuthorization(
-                    hasFullCrossUsersPermission(caller, userHandle) || isCameraServerUid(caller));
-            if (parent) {
-                Preconditions.checkCallAuthorization(
-                        isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()));
-            }
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        Preconditions.checkCallAuthorization(
+                hasFullCrossUsersPermission(caller, userHandle)
+                        || isCameraServerUid(caller)
+                        || hasPermission(MANAGE_DEVICE_POLICY_CAMERA,
+                            caller.getPackageName(), userHandle)
+                        || hasPermission(QUERY_ADMIN_POLICY, caller.getPackageName()));
 
         int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
-
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            PolicyDefinition<Boolean> policy =
-                    PolicyDefinition.getPolicyDefinitionForUserRestriction(
-                            UserManager.DISALLOW_CAMERA);
-            if (who != null) {
-                EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackageName);
-                return Boolean.TRUE.equals(
-                        mDevicePolicyEngine.getLocalPolicySetByAdmin(
-                                policy, admin, affectedUserId));
-            } else {
-                return Boolean.TRUE.equals(
-                        mDevicePolicyEngine.getResolvedPolicy(policy, affectedUserId));
-            }
+        PolicyDefinition<Boolean> policy =
+                PolicyDefinition.getPolicyDefinitionForUserRestriction(
+                        UserManager.DISALLOW_CAMERA);
+        if (who != null) {
+            EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackageName);
+            return Boolean.TRUE.equals(
+                    mDevicePolicyEngine.getLocalPolicySetByAdmin(
+                            policy, admin, affectedUserId));
         } else {
-            synchronized (getLockObject()) {
-                if (who != null) {
-                    ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
-                    return (admin != null) && admin.disableCamera;
-                }
-                // First, see if DO has set it.  If so, it's device-wide.
-                final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
-                if (deviceOwner != null && deviceOwner.disableCamera) {
-                    return true;
-                }
-
-                // Return the strictest policy across all participating admins.
-                List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(affectedUserId);
-
-                // Determine whether or not the device camera is disabled for any active admins.
-                for (ActiveAdmin activeAdmin : admins) {
-                    if (activeAdmin.disableCamera) {
-                        return true;
-                    }
-                }
-                return false;
-            }
+            return Boolean.TRUE.equals(
+                    mDevicePolicyEngine.getResolvedPolicy(policy, affectedUserId));
         }
     }
 
@@ -10117,9 +9855,6 @@
         clearUserPoliciesLocked(userId);
         clearOverrideApnUnchecked();
         clearApplicationRestrictions(userId);
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            mInjector.getPackageManagerInternal().clearBlockUninstallForUser(userId);
-        }
 
         mOwners.clearDeviceOwner();
         mOwners.writeDeviceOwner();
@@ -10131,16 +9866,11 @@
         setNetworkLoggingActiveInternal(false);
         deleteTransferOwnershipBundleLocked(userId);
         toggleBackupServiceActive(UserHandle.USER_SYSTEM, true);
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            pushUserControlDisabledPackagesLocked(userId);
-        }
         setGlobalSettingDeviceOwnerType(DEVICE_OWNER_TYPE_DEFAULT);
 
-        if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
-            mDevicePolicyEngine.removePoliciesForAdmin(
-                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
-                            admin.info.getComponent(), userId, admin));
-        }
+        mDevicePolicyEngine.removePoliciesForAdmin(
+                EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                        admin.info.getComponent(), userId, admin));
     }
 
     private void clearApplicationRestrictions(int userId) {
@@ -10289,11 +10019,9 @@
         applyProfileRestrictionsIfDeviceOwnerLocked();
         setNetworkLoggingActiveInternal(false);
 
-        if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
-            mDevicePolicyEngine.removePoliciesForAdmin(
-                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
-                            admin.info.getComponent(), userId, admin));
-        }
+        mDevicePolicyEngine.removePoliciesForAdmin(
+                EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                        admin.info.getComponent(), userId, admin));
     }
 
     @Override
@@ -10337,9 +10065,6 @@
         policy.mAffiliationIds.clear();
         resetAffiliationCacheLocked();
         policy.mLockTaskPackages.clear();
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userId);
-        }
         policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
         saveSettingsLocked(userId);
 
@@ -10347,7 +10072,6 @@
             mIPermissionManager.updatePermissionFlagsForAllApps(
                     PackageManager.FLAG_PERMISSION_POLICY_FIXED,
                     0  /* flagValues */, userId);
-            pushUserRestrictions(userId);
         } catch (RemoteException re) {
             // Shouldn't happen.
             Slogf.wtf(LOG_TAG, "Failing in updatePermissionFlagsForAllApps", re);
@@ -11434,53 +11158,30 @@
     @Override
     public void addPersistentPreferredActivity(ComponentName who, String callerPackageName,
             IntentFilter filter, ComponentName activity) {
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
 
         final int userId = caller.getUserId();
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin;
-            if (who == null) {
-                enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                        who,
-                        MANAGE_DEVICE_POLICY_LOCK_TASK,
-                        caller.getPackageName(),
-                        userId);
-            } else {
-                Preconditions.checkCallAuthorization(isProfileOwner(caller)
-                        || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
-                enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
-            }
-            if (!isPackageInstalledForUser(activity.getPackageName(), userId)) {
-                // Fail early as packageManager doesn't persist the activity if its not installed.
-                return;
-            }
-            mDevicePolicyEngine.setLocalPolicy(
-                    PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter),
-                    enforcingAdmin,
-                    new ComponentNamePolicyValue(activity),
+        EnforcingAdmin enforcingAdmin;
+        if (who == null) {
+            enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                    who,
+                    MANAGE_DEVICE_POLICY_LOCK_TASK,
+                    caller.getPackageName(),
                     userId);
         } else {
-            Objects.requireNonNull(who, "ComponentName is null");
             Preconditions.checkCallAuthorization(isProfileOwner(caller)
                     || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
-            synchronized (getLockObject()) {
-                long id = mInjector.binderClearCallingIdentity();
-                try {
-                    mIPackageManager.addPersistentPreferredActivity(filter, activity, userId);
-                    mIPackageManager.flushPackageRestrictionsAsUser(userId);
-                } catch (RemoteException re) {
-                    // Shouldn't happen
-                    Slog.wtf(LOG_TAG, "Error adding persistent preferred activity", re);
-                } finally {
-                    mInjector.binderRestoreCallingIdentity(id);
-                }
-            }
+            enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
         }
+        if (!isPackageInstalledForUser(activity.getPackageName(), userId)) {
+            // Fail early as packageManager doesn't persist the activity if its not installed.
+            return;
+        }
+        mDevicePolicyEngine.setLocalPolicy(
+                PolicyDefinition.PERSISTENT_PREFERRED_ACTIVITY(filter),
+                enforcingAdmin,
+                new ComponentNamePolicyValue(activity),
+                userId);
         final String activityPackage =
                 (activity != null ? activity.getPackageName() : null);
         DevicePolicyEventLogger
@@ -11493,51 +11194,25 @@
     @Override
     public void clearPackagePersistentPreferredActivities(ComponentName who,
             String callerPackageName, String packageName) {
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         final int userId = caller.getUserId();
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin;
-            if (who == null) {
-                enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                        who,
-                        MANAGE_DEVICE_POLICY_LOCK_TASK,
-                        caller.getPackageName(),
-                        userId);
-            } else {
-                Preconditions.checkCallAuthorization(isProfileOwner(caller)
-                        || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
-                enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
-            }
-            clearPackagePersistentPreferredActivitiesFromPolicyEngine(
-                    enforcingAdmin,
-                    packageName,
+        EnforcingAdmin enforcingAdmin;
+        if (who == null) {
+            enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                    who,
+                    MANAGE_DEVICE_POLICY_LOCK_TASK,
+                    caller.getPackageName(),
                     userId);
         } else {
-            Objects.requireNonNull(who, "ComponentName is null");
             Preconditions.checkCallAuthorization(isProfileOwner(caller)
                     || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
-            synchronized (getLockObject()) {
-                long id = mInjector.binderClearCallingIdentity();
-                try {
-                    mIPackageManager.clearPackagePersistentPreferredActivities(packageName,
-                            userId);
-                    mIPackageManager.flushPackageRestrictionsAsUser(userId);
-                } catch (RemoteException re) {
-                    // Shouldn't happen
-                    Slogf.wtf(
-                            LOG_TAG, "Error when clearing package persistent preferred activities",
-                            re);
-                } finally {
-                    mInjector.binderRestoreCallingIdentity(id);
-                }
-            }
+            enforcingAdmin = getEnforcingAdminForCaller(who, callerPackageName);
         }
+        clearPackagePersistentPreferredActivitiesFromPolicyEngine(
+                enforcingAdmin,
+                packageName,
+                userId);
     }
 
     /**
@@ -12274,28 +11949,15 @@
             return false;
         }
 
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-            Objects.requireNonNull(who, "ComponentName is null");
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
 
         int userId = getProfileParentUserIfRequested(
                 caller.getUserId(), calledOnParentInstance);
         if (calledOnParentInstance) {
-            if (!isPolicyEngineForFinanceFlagEnabled()) {
-                Preconditions.checkCallAuthorization(
-                        isProfileOwnerOfOrganizationOwnedDevice(caller));
-            }
             Preconditions.checkArgument(packageList == null || packageList.isEmpty(),
                     "Permitted input methods must allow all input methods or only "
                             + "system input methods when called on the parent instance of an "
                             + "organization-owned device");
-        } else if (!isPolicyEngineForFinanceFlagEnabled()) {
-            Preconditions.checkCallAuthorization(
-                    isDefaultDeviceOwner(caller) || isProfileOwner(caller));
         }
 
         if (packageList != null) {
@@ -12320,28 +11982,20 @@
         }
 
         synchronized (getLockObject()) {
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
-                        who, MANAGE_DEVICE_POLICY_INPUT_METHODS,
-                        caller.getPackageName(), userId);
-                if (packageList == null) {
-                    mDevicePolicyEngine.removeLocalPolicy(
-                            PolicyDefinition.PERMITTED_INPUT_METHODS,
-                            admin,
-                            userId);
-                } else {
-                    mDevicePolicyEngine.setLocalPolicy(
-                            PolicyDefinition.PERMITTED_INPUT_METHODS,
-                            admin,
-                            new StringSetPolicyValue(new HashSet<>(packageList)),
-                            userId);
-                }
+            EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+                    who, MANAGE_DEVICE_POLICY_INPUT_METHODS,
+                    caller.getPackageName(), userId);
+            if (packageList == null) {
+                mDevicePolicyEngine.removeLocalPolicy(
+                        PolicyDefinition.PERMITTED_INPUT_METHODS,
+                        admin,
+                        userId);
             } else {
-                ActiveAdmin admin = getParentOfAdminIfRequired(
-                        getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()),
-                        calledOnParentInstance);
-                admin.permittedInputMethods = packageList;
-                saveSettingsLocked(caller.getUserId());
+                mDevicePolicyEngine.setLocalPolicy(
+                        PolicyDefinition.PERMITTED_INPUT_METHODS,
+                        admin,
+                        new StringSetPolicyValue(new HashSet<>(packageList)),
+                        userId);
             }
         }
 
@@ -12371,37 +12025,14 @@
             return null;
         }
 
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-            Objects.requireNonNull(who, "ComponentName is null");
-        }
-
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            if (calledOnParentInstance) {
-                Preconditions.checkCallAuthorization(
-                        isProfileOwnerOfOrganizationOwnedDevice(caller));
-            } else {
-                Preconditions.checkCallAuthorization(
-                        isDefaultDeviceOwner(caller) || isProfileOwner(caller));
-            }
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
 
         synchronized (getLockObject()) {
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                int affectedUser = calledOnParentInstance ? getProfileParentId(
-                        caller.getUserId()) : caller.getUserId();
-                Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
-                        PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
-                return policy == null ? null : new ArrayList<>(policy);
-            } else {
-                ActiveAdmin admin = getParentOfAdminIfRequired(
-                        getProfileOwnerOrDeviceOwnerLocked(
-                                caller.getUserId()), calledOnParentInstance);
-                return admin.permittedInputMethods;
-            }
+            int affectedUser = calledOnParentInstance ? getProfileParentId(
+                    caller.getUserId()) : caller.getUserId();
+            Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
+                    PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
+            return policy == null ? null : new ArrayList<>(policy);
         }
     }
 
@@ -12419,29 +12050,9 @@
     }
 
     private @Nullable List<String> getPermittedInputMethodsUnchecked(@UserIdInt int userId) {
-        List<String> result = null;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.PERMITTED_INPUT_METHODS, userId);
-            result = policy == null ? null : new ArrayList<>(policy);
-        } else {
-            synchronized (getLockObject()) {
-                // Only device or profile owners can have permitted lists set.
-                List<ActiveAdmin> admins =
-                        getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(
-                                userId);
-                for (ActiveAdmin admin : admins) {
-                    List<String> fromAdmin = admin.permittedInputMethods;
-                    if (fromAdmin != null) {
-                        if (result == null) {
-                            result = new ArrayList<String>(fromAdmin);
-                        } else {
-                            result.retainAll(fromAdmin);
-                        }
-                    }
-                }
-            }
-        }
+        Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.PERMITTED_INPUT_METHODS, userId);
+        List<String> result = policy == null ? null : new ArrayList<>(policy);
 
         // If we have a permitted list add all system input methods.
         if (result != null) {
@@ -12472,39 +12083,23 @@
                 String.format(NOT_SYSTEM_CALLER_MSG,
                         "query if an input method is disabled by admin"));
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            int affectedUser = calledOnParentInstance ? getProfileParentId(userHandle) : userHandle;
-            Map<EnforcingAdmin, PolicyValue<Set<String>>> policies =
-                    mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
-                            PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
-            EnforcingAdmin admin = null;
-            for (EnforcingAdmin a : policies.keySet()) {
-                if (a.getPackageName().equals(who.getPackageName())) {
-                    if (policies.get(a).getValue() == null) {
-                        return true;
-                    } else {
-                        return checkPackagesInPermittedListOrSystem(
-                                Collections.singletonList(packageName),
-                                new ArrayList<>(policies.get(a).getValue()), affectedUser);
-                    }
-                }
-            }
-            // Admin didn't set a policy
-            return false;
-        } else {
-            synchronized (getLockObject()) {
-                ActiveAdmin admin = getParentOfAdminIfRequired(
-                        getActiveAdminUncheckedLocked(who, userHandle), calledOnParentInstance);
-                if (admin == null) {
-                    return false;
-                }
-                if (admin.permittedInputMethods == null) {
+        int affectedUser = calledOnParentInstance ? getProfileParentId(userHandle) : userHandle;
+        Map<EnforcingAdmin, PolicyValue<Set<String>>> policies =
+                mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+                        PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
+        for (EnforcingAdmin a : policies.keySet()) {
+            if (a.getPackageName().equals(who.getPackageName())) {
+                if (policies.get(a).getValue() == null) {
                     return true;
+                } else {
+                    return checkPackagesInPermittedListOrSystem(
+                            Collections.singletonList(packageName),
+                            new ArrayList<>(policies.get(a).getValue()), affectedUser);
                 }
-                return checkPackagesInPermittedListOrSystem(Collections.singletonList(packageName),
-                        admin.permittedInputMethods, userHandle);
             }
         }
+        // Admin didn't set a policy
+        return false;
     }
 
     @Override
@@ -12775,12 +12370,9 @@
                     + ", token=" + token);
         }
 
+        mDevicePolicyEngine.handleUserCreated(user);
+
         final int userId = user.id;
-
-        if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
-            mDevicePolicyEngine.handleUserCreated(user);
-        }
-
         if (token != null) {
             synchronized (getLockObject()) {
                 if (mPendingUserCreatedCallbackTokens.contains(token)) {
@@ -13374,79 +12966,54 @@
             ComponentName who, String callerPackage, String key, boolean enabledFromThisOwner,
             boolean parent) {
 
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackage);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackage);
         int userId = caller.getUserId();
         int affectedUserId = parent ? getProfileParentId(userId) : userId;
 
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            if (!isDeviceOwner(caller) && !isProfileOwner(caller)) {
-                EnforcingAdmin admin = enforcePermissionForUserRestriction(
-                        who,
-                        key,
-                        caller.getPackageName(),
-                        affectedUserId);
-                if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) {
-                    throw new IllegalStateException("Calling package is not targeting Android U.");
-                }
-                if (!UserRestrictionsUtils.isValidRestriction(key)) {
-                    throw new IllegalArgumentException("Invalid restriction key: " + key);
-                }
-                PolicyDefinition<Boolean> policyDefinition =
-                        PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
-                if (enabledFromThisOwner) {
-                    setLocalUserRestrictionInternal(
-                            admin, key, /* enabled= */ true, affectedUserId);
-                } else {
-                    // Remove any local and global policy that was set by the admin
-                    if (!policyDefinition.isLocalOnlyPolicy()) {
-                        setGlobalUserRestrictionInternal(admin, key, /* enabled= */ false);
-                    }
-                    if (!policyDefinition.isGlobalOnlyPolicy()) {
-                        setLocalUserRestrictionInternal(admin, key, /* enabled= */ false,
-                                userId);
-
-                        int parentUserId = getProfileParentId(userId);
-                        if (parentUserId != userId) {
-                            setLocalUserRestrictionInternal(
-                                    admin, key, /* enabled= */ false, parentUserId);
-                        }
-                    }
-                }
+        if (!isDeviceOwner(caller) && !isProfileOwner(caller)) {
+            EnforcingAdmin admin = enforcePermissionForUserRestriction(
+                    who,
+                    key,
+                    caller.getPackageName(),
+                    affectedUserId);
+            if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) {
+                throw new IllegalStateException("Calling package is not targeting Android U.");
+            }
+            if (!UserRestrictionsUtils.isValidRestriction(key)) {
+                throw new IllegalArgumentException("Invalid restriction key: " + key);
+            }
+            PolicyDefinition<Boolean> policyDefinition =
+                    PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
+            if (enabledFromThisOwner) {
+                setLocalUserRestrictionInternal(
+                        admin, key, /* enabled= */ true, affectedUserId);
             } else {
-                if (!UserRestrictionsUtils.isValidRestriction(key)) {
-                    return;
+                // Remove any local and global policy that was set by the admin
+                if (!policyDefinition.isLocalOnlyPolicy()) {
+                    setGlobalUserRestrictionInternal(admin, key, /* enabled= */ false);
                 }
-                Objects.requireNonNull(who, "ComponentName is null");
-                EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
-                checkAdminCanSetRestriction(caller, parent, key);
-                setBackwardCompatibleUserRestriction(
-                        caller, admin, key, enabledFromThisOwner, parent);
+                if (!policyDefinition.isGlobalOnlyPolicy()) {
+                    setLocalUserRestrictionInternal(admin, key, /* enabled= */ false,
+                            userId);
+
+                    int parentUserId = getProfileParentId(userId);
+                    if (parentUserId != userId) {
+                        setLocalUserRestrictionInternal(
+                                admin, key, /* enabled= */ false, parentUserId);
+                    }
+                }
             }
         } else {
             if (!UserRestrictionsUtils.isValidRestriction(key)) {
                 return;
             }
             Objects.requireNonNull(who, "ComponentName is null");
+            EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
             checkAdminCanSetRestriction(caller, parent, key);
-            synchronized (getLockObject()) {
-                final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
-                        getProfileOwnerOrDeviceOwnerLocked(userId), parent);
-                // Save the restriction to ActiveAdmin.
-                final Bundle restrictions = activeAdmin.ensureUserRestrictions();
-                if (enabledFromThisOwner) {
-                    restrictions.putBoolean(key, true);
-                } else {
-                    restrictions.remove(key);
-                }
-                saveUserRestrictionsLocked(userId);
-            }
+            setBackwardCompatibleUserRestriction(
+                    caller, admin, key, enabledFromThisOwner, parent);
         }
         logUserRestrictionCall(key, enabledFromThisOwner, parent, caller);
     }
@@ -13532,9 +13099,6 @@
 
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
 
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            throw new IllegalStateException("Feature flag is not enabled.");
-        }
         if (isDeviceOwner(caller) || isProfileOwner(caller)) {
             throw new SecurityException("Admins are not allowed to call this API.");
         }
@@ -13604,121 +13168,33 @@
                 key, enabled, caller.toString());
     }
 
-    private void saveUserRestrictionsLocked(int userId) {
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            // User restrictions are handled in the policy engine
-            return;
-        }
-        saveSettingsLocked(userId);
-        pushUserRestrictions(userId);
-        sendChangedNotification(userId);
-    }
-
-    /**
-     * Pushes the user restrictions originating from a specific user.
-     *
-     * If called by the profile owner of an organization-owned device, the global and local
-     * user restrictions will be an accumulation of the global user restrictions from the profile
-     * owner active admin and its parent active admin. The key of the local user restrictions set
-     * will be the target user id.
-     */
-    private void pushUserRestrictions(int originatingUserId) {
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            // User restrictions are handled in the policy engine
-            return;
-        }
-        final Bundle global;
-        final RestrictionsSet local = new RestrictionsSet();
-        final boolean isDeviceOwner;
-        synchronized (getLockObject()) {
-            isDeviceOwner = mOwners.isDeviceOwnerUserId(originatingUserId);
-            if (isDeviceOwner) {
-                final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
-                if (deviceOwner == null) {
-                    return; // Shouldn't happen.
-                }
-                global = deviceOwner.getGlobalUserRestrictions(OWNER_TYPE_DEVICE_OWNER);
-                local.updateRestrictions(originatingUserId, deviceOwner.getLocalUserRestrictions(
-                        OWNER_TYPE_DEVICE_OWNER));
-            } else {
-                final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(originatingUserId);
-                if (profileOwner == null) {
-                    return;
-                }
-                global = profileOwner.getGlobalUserRestrictions(OWNER_TYPE_PROFILE_OWNER);
-                local.updateRestrictions(originatingUserId, profileOwner.getLocalUserRestrictions(
-                        OWNER_TYPE_PROFILE_OWNER));
-                // Global (device-wide) and local user restrictions set by the profile owner of an
-                // organization-owned device are stored in the parent ActiveAdmin instance.
-                if (isProfileOwnerOfOrganizationOwnedDevice(
-                        profileOwner.getUserHandle().getIdentifier())) {
-                    // The global restrictions set on the parent ActiveAdmin instance need to be
-                    // merged with the global restrictions set on the profile owner ActiveAdmin
-                    // instance, since both are to be applied device-wide.
-                    UserRestrictionsUtils.merge(global,
-                            profileOwner.getParentActiveAdmin().getGlobalUserRestrictions(
-                                    OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
-                    // The local restrictions set on the parent ActiveAdmin instance are only to be
-                    // applied to the primary user. They therefore need to be added the local
-                    // restriction set with the primary user id as the key, in this case the
-                    // primary user id is the target user.
-                    local.updateRestrictions(
-                            getProfileParentId(profileOwner.getUserHandle().getIdentifier()),
-                            profileOwner.getParentActiveAdmin().getLocalUserRestrictions(
-                                    OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
-                }
-            }
-        }
-        mUserManagerInternal.setDevicePolicyUserRestrictions(originatingUserId, global, local,
-                isDeviceOwner);
-    }
-
     @Override
     public Bundle getUserRestrictions(ComponentName who, String callerPackage, boolean parent) {
         if (!mHasFeature) {
             return null;
         }
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackage);
-        } else {
-            caller = getCallerIdentity(who);
-        }
-
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            int targetUserId = parent
-                    ? getProfileParentId(caller.getUserId()) : caller.getUserId();
-            EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
-            if (isDeviceOwner(caller) || isProfileOwner(caller)) {
-                Objects.requireNonNull(who, "ComponentName is null");
-                Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
-                        || isFinancedDeviceOwner(caller)
-                        || isProfileOwner(caller)
-                        || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
-
-                Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId);
-                // Add global restrictions set by the admin as well.
-                restrictions.putAll(
-                        getUserRestrictionsFromPolicyEngine(admin, UserHandle.USER_ALL));
-                return restrictions;
-            } else {
-                if (!mInjector.isChangeEnabled(
-                        ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) {
-                    throw new IllegalStateException("Calling package is not targeting Android U.");
-                }
-                return getUserRestrictionsFromPolicyEngine(admin, targetUserId);
-            }
-        } else {
+        CallerIdentity caller = getCallerIdentity(who, callerPackage);
+        int targetUserId = parent
+                ? getProfileParentId(caller.getUserId()) : caller.getUserId();
+        EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
+        if (isDeviceOwner(caller) || isProfileOwner(caller)) {
             Objects.requireNonNull(who, "ComponentName is null");
             Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
                     || isFinancedDeviceOwner(caller)
                     || isProfileOwner(caller)
                     || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
-            synchronized (getLockObject()) {
-                final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
-                        getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
-                return activeAdmin.userRestrictions;
+
+            Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId);
+            // Add global restrictions set by the admin as well.
+            restrictions.putAll(
+                    getUserRestrictionsFromPolicyEngine(admin, UserHandle.USER_ALL));
+            return restrictions;
+        } else {
+            if (!mInjector.isChangeEnabled(
+                    ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) {
+                throw new IllegalStateException("Calling package is not targeting Android U.");
             }
+            return getUserRestrictionsFromPolicyEngine(admin, targetUserId);
         }
     }
 
@@ -13889,10 +13365,6 @@
             return null;
         }
         final CallerIdentity caller = getCallerIdentity(callerPackage);
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            throw new IllegalStateException("Feature flag is not enabled.");
-        }
-
         EnforcingAdmin admin = getEnforcingAdminForCaller(/*who=*/ null, caller.getPackageName());
 
         return getUserRestrictionsFromPolicyEngine(admin,
@@ -13922,13 +13394,7 @@
             boolean hidden, boolean parent) {
         CallerIdentity caller = getCallerIdentity(who, callerPackage);
         final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
-        } else {
-            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
-                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
-                    || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
-        }
+        enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
 
         List<String> exemptApps = listPolicyExemptAppsUnchecked(mContext);
         if (exemptApps.contains(packageName)) {
@@ -13940,11 +13406,6 @@
         boolean result;
         synchronized (getLockObject()) {
             if (parent) {
-                if (!isPolicyEngineForFinanceFlagEnabled()) {
-                    Preconditions.checkCallAuthorization(
-                            isProfileOwnerOfOrganizationOwnedDevice(
-                                    caller.getUserId()) && isManagedProfile(caller.getUserId()));
-                }
                 // Ensure the package provided is a system package, this is to ensure that this
                 // API cannot be used to leak if certain non-system package exists in the person
                 // profile.
@@ -13957,29 +13418,24 @@
                 Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)",
                         packageName, hidden, userId);
             }
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
-                mDevicePolicyEngine.setLocalPolicy(
-                        PolicyDefinition.APPLICATION_HIDDEN(packageName),
-                        admin,
-                        new BooleanPolicyValue(hidden),
-                        userId);
-                result = mInjector.binderWithCleanCallingIdentity(() -> {
-                    try {
-                        // This is a best effort to continue returning the same value that was
-                        // returned before the policy engine migration.
-                        return mInjector.getIPackageManager().getPackageInfo(
-                                packageName, MATCH_UNINSTALLED_PACKAGES, userId) != null
-                                && (mIPackageManager.getApplicationHiddenSettingAsUser(
-                                        packageName, userId) == hidden);
-                    } catch (RemoteException e) {
-                        return false;
-                    }
-                });
-            } else {
-                result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager
-                        .setApplicationHiddenSettingAsUser(packageName, hidden, userId));
-            }
+            EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
+            mDevicePolicyEngine.setLocalPolicy(
+                    PolicyDefinition.APPLICATION_HIDDEN(packageName),
+                    admin,
+                    new BooleanPolicyValue(hidden),
+                    userId);
+            result = mInjector.binderWithCleanCallingIdentity(() -> {
+                try {
+                    // This is a best effort to continue returning the same value that was
+                    // returned before the policy engine migration.
+                    return mInjector.getIPackageManager().getPackageInfo(
+                            packageName, MATCH_UNINSTALLED_PACKAGES, userId) != null
+                            && (mIPackageManager.getApplicationHiddenSettingAsUser(
+                                    packageName, userId) == hidden);
+                } catch (RemoteException e) {
+                    return false;
+                }
+            });
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN)
@@ -13996,23 +13452,11 @@
             String packageName, boolean parent) {
         CallerIdentity caller = getCallerIdentity(who, callerPackage);
         int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            // TODO: Also support DELEGATION_PACKAGE_ACCESS
-            enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
-        } else {
-            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
-                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
-                    || (caller.hasPackage() && isCallerDelegate(
-                            caller, DELEGATION_PACKAGE_ACCESS)));
-        }
+        // TODO: Also support DELEGATION_PACKAGE_ACCESS
+        enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
 
         synchronized (getLockObject()) {
             if (parent) {
-                if (!isPolicyEngineForFinanceFlagEnabled()) {
-                    Preconditions.checkCallAuthorization(
-                            isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())
-                                    && isManagedProfile(caller.getUserId()));
-                }
                 // Ensure the package provided is a system package.
                 mInjector.binderWithCleanCallingIdentity(() ->
                         enforcePackageIsSystemPackage(packageName, userId));
@@ -14199,57 +13643,26 @@
 
         enforceMaxStringLength(accountType, "account type");
 
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         synchronized (getLockObject()) {
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                int affectedUser = getAffectedUser(parent);
-                EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                        who,
-                        MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
-                        caller.getPackageName(),
-                        affectedUser
-                );
-                if (disabled) {
-                    mDevicePolicyEngine.setLocalPolicy(
-                            PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
-                            enforcingAdmin,
-                            new BooleanPolicyValue(disabled),
-                            affectedUser);
-                } else {
-                    mDevicePolicyEngine.removeLocalPolicy(
-                            PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
-                            enforcingAdmin,
-                            affectedUser);
-                }
+            int affectedUser = getAffectedUser(parent);
+            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                    who,
+                    MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+                    caller.getPackageName(),
+                    affectedUser
+            );
+            if (disabled) {
+                mDevicePolicyEngine.setLocalPolicy(
+                        PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+                        enforcingAdmin,
+                        new BooleanPolicyValue(disabled),
+                        affectedUser);
             } else {
-                final ActiveAdmin ap;
-                Objects.requireNonNull(who, "ComponentName is null");
-                /*
-                 * When called on the parent DPM instance (parent == true), affects active admin
-                 * selection in two ways:
-                 * * The ActiveAdmin must be of an org-owned profile owner.
-                 * * The parent ActiveAdmin instance should be used for managing the restriction.
-                 */
-                if (parent) {
-                    ap = getParentOfAdminIfRequired(getOrganizationOwnedProfileOwnerLocked(caller),
-                            parent);
-                } else {
-                    Preconditions.checkCallAuthorization(
-                            isDefaultDeviceOwner(caller) || isProfileOwner(caller));
-                    ap = getParentOfAdminIfRequired(
-                            getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
-                }
-                if (disabled) {
-                    ap.accountTypesWithManagementDisabled.add(accountType);
-                } else {
-                    ap.accountTypesWithManagementDisabled.remove(accountType);
-                }
-                saveSettingsLocked(UserHandle.getCallingUserId());
+                mDevicePolicyEngine.removeLocalPolicy(
+                        PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+                        enforcingAdmin,
+                        affectedUser);
             }
         }
     }
@@ -14266,62 +13679,35 @@
         if (!mHasFeature) {
             return null;
         }
-        CallerIdentity caller;
+        CallerIdentity caller = getCallerIdentity(callerPackageName);
         Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
         final ArraySet<String> resultSet = new ArraySet<>();
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            int affectedUser = parent ? getProfileParentId(userId) : userId;
-            caller = getCallerIdentity(callerPackageName);
-            if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
-                    callerPackageName, affectedUser)
-                    && !hasFullCrossUsersPermission(caller, userId)) {
-                throw new SecurityException("Caller does not have permission to call this on user: "
-                        + affectedUser);
+        int affectedUser = parent ? getProfileParentId(userId) : userId;
+        if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+                callerPackageName, affectedUser)
+                && !hasFullCrossUsersPermission(caller, userId)) {
+            throw new SecurityException("Caller does not have permission to call this on user: "
+                    + affectedUser);
+        }
+        Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins(
+                PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED,
+                affectedUser);
+
+        for (PolicyKey key : keys) {
+            if (!(key instanceof AccountTypePolicyKey)) {
+                throw new IllegalStateException("PolicyKey for "
+                        + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT is not of type "
+                        + "AccountTypePolicyKey");
             }
-            Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins(
-                    PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED,
+            AccountTypePolicyKey parsedKey =
+                    (AccountTypePolicyKey) key;
+            String accountType = Objects.requireNonNull(parsedKey.getAccountType());
+
+            Boolean disabled = mDevicePolicyEngine.getResolvedPolicy(
+                    PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
                     affectedUser);
-
-            for (PolicyKey key : keys) {
-                if (!(key instanceof AccountTypePolicyKey)) {
-                    throw new IllegalStateException("PolicyKey for "
-                            + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT is not of type "
-                            + "AccountTypePolicyKey");
-                }
-                AccountTypePolicyKey parsedKey =
-                        (AccountTypePolicyKey) key;
-                String accountType = Objects.requireNonNull(parsedKey.getAccountType());
-
-                Boolean disabled = mDevicePolicyEngine.getResolvedPolicy(
-                        PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
-                        affectedUser);
-                if (disabled != null && disabled) {
-                    resultSet.add(accountType);
-                }
-            }
-        } else {
-            caller = getCallerIdentity();
-            Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
-
-            synchronized (getLockObject()) {
-                if (!parent) {
-                    final DevicePolicyData policy = getUserData(userId);
-                    for (ActiveAdmin admin : policy.mAdminList) {
-                        resultSet.addAll(admin.accountTypesWithManagementDisabled);
-                    }
-                }
-
-                // Check if there's a profile owner of an org-owned device and the method is called
-                // for the parent user of this profile owner.
-                final ActiveAdmin orgOwnedAdmin =
-                        getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
-                final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent
-                        || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId);
-                if (shouldGetParentAccounts) {
-                    resultSet.addAll(
-                            orgOwnedAdmin.getParentActiveAdmin()
-                                    .accountTypesWithManagementDisabled);
-                }
+            if (disabled != null && disabled) {
+                resultSet.add(accountType);
             }
         }
         return resultSet.toArray(new String[resultSet.size()]);
@@ -14332,46 +13718,19 @@
             boolean uninstallBlocked) {
         final CallerIdentity caller = getCallerIdentity(who, callerPackage);
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
-                    who,
-                    new String[]{
-                            MANAGE_DEVICE_POLICY_APPS_CONTROL,
-                            MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL
-                    },
-                    caller.getPackageName(),
-                    caller.getUserId());
-            mDevicePolicyEngine.setLocalPolicy(
-                    PolicyDefinition.PACKAGE_UNINSTALL_BLOCKED(packageName),
-                    enforcingAdmin,
-                    new BooleanPolicyValue(uninstallBlocked),
-                    caller.getUserId());
-        } else {
-            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
-                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
-                    || isFinancedDeviceOwner(caller)))
-                    || (caller.hasPackage()
-                    && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL)));
-            final int userId = caller.getUserId();
-            synchronized (getLockObject()) {
-                long id = mInjector.binderClearCallingIdentity();
-                try {
-                    mIPackageManager.setBlockUninstallForUser(
-                            packageName, uninstallBlocked, userId);
-                } catch (RemoteException re) {
-                    // Shouldn't happen.
-                    Slogf.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
-                } finally {
-                    mInjector.binderRestoreCallingIdentity(id);
-                }
-            }
-            if (uninstallBlocked) {
-                final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
-                pmi.removeNonSystemPackageSuspensions(packageName, userId);
-                pmi.removeDistractingPackageRestrictions(packageName, userId);
-                pmi.flushPackageRestrictions(userId);
-            }
-        }
+        EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
+                who,
+                new String[]{
+                        MANAGE_DEVICE_POLICY_APPS_CONTROL,
+                        MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL
+                },
+                caller.getPackageName(),
+                caller.getUserId());
+        mDevicePolicyEngine.setLocalPolicy(
+                PolicyDefinition.PACKAGE_UNINSTALL_BLOCKED(packageName),
+                enforcingAdmin,
+                new BooleanPolicyValue(uninstallBlocked),
+                caller.getUserId());
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_UNINSTALL_BLOCKED)
@@ -14898,49 +14257,35 @@
             enforceMaxPackageNameLength(pkg);
         }
 
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin;
-            synchronized (getLockObject()) {
-                enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
-            }
-            LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+        EnforcingAdmin enforcingAdmin;
+        synchronized (getLockObject()) {
+            enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
+        }
+        LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+                PolicyDefinition.LOCK_TASK,
+                enforcingAdmin,
+                caller.getUserId());
+        LockTaskPolicy policy;
+        if (currentPolicy == null) {
+            policy = new LockTaskPolicy(Set.of(packages));
+        } else {
+            policy = new LockTaskPolicy(currentPolicy);
+            policy.setPackages(Set.of(packages));
+        }
+        if (policy.getPackages().isEmpty()) {
+            mDevicePolicyEngine.removeLocalPolicy(
                     PolicyDefinition.LOCK_TASK,
                     enforcingAdmin,
                     caller.getUserId());
-            LockTaskPolicy policy;
-            if (currentPolicy == null) {
-                policy = new LockTaskPolicy(Set.of(packages));
-            } else {
-                policy = new LockTaskPolicy(currentPolicy);
-                policy.setPackages(Set.of(packages));
-            }
-            if (policy.getPackages().isEmpty()) {
-                mDevicePolicyEngine.removeLocalPolicy(
-                        PolicyDefinition.LOCK_TASK,
-                        enforcingAdmin,
-                        caller.getUserId());
-            } else {
-                mDevicePolicyEngine.setLocalPolicy(
-                        PolicyDefinition.LOCK_TASK,
-                        enforcingAdmin,
-                        policy,
-                        caller.getUserId());
-            }
         } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            synchronized (getLockObject()) {
-                enforceCanCallLockTaskLocked(caller);
-                final int userHandle = caller.getUserId();
-                setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
-            }
+            mDevicePolicyEngine.setLocalPolicy(
+                    PolicyDefinition.LOCK_TASK,
+                    enforcingAdmin,
+                    policy,
+                    caller.getUserId());
         }
     }
 
@@ -14955,32 +14300,18 @@
 
     @Override
     public String[] getLockTaskPackages(ComponentName who, String callerPackageName) {
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         final int userHandle = caller.getUserId();
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            synchronized (getLockObject()) {
-                enforceCanQueryLockTaskLocked(who, caller.getPackageName());
-            }
-            LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.LOCK_TASK, userHandle);
-            if (policy == null) {
-                return new String[0];
-            } else {
-                return policy.getPackages().toArray(new String[policy.getPackages().size()]);
-            }
+        synchronized (getLockObject()) {
+            enforceCanQueryLockTaskLocked(who, caller.getPackageName());
+        }
+        LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.LOCK_TASK, userHandle);
+        if (policy == null) {
+            return new String[0];
         } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            synchronized (getLockObject()) {
-                enforceCanCallLockTaskLocked(caller);
-                final List<String> packages = getUserData(userHandle).mLockTaskPackages;
-                return packages.toArray(new String[packages.size()]);
-            }
+            return policy.getPackages().toArray(new String[policy.getPackages().size()]);
         }
     }
 
@@ -14996,18 +14327,12 @@
         }
 
         final int userId = mInjector.userHandleGetCallingUserId();
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.LOCK_TASK, userId);
-            if (policy == null) {
-                return false;
-            }
-            return policy.getPackages().contains(pkg);
-        } else {
-            synchronized (getLockObject()) {
-                return getUserData(userId).mLockTaskPackages.contains(pkg);
-            }
+        LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.LOCK_TASK, userId);
+        if (policy == null) {
+            return false;
         }
+        return policy.getPackages().contains(pkg);
     }
 
     @Override
@@ -15021,54 +14346,40 @@
         Preconditions.checkArgument(hasHome || !hasNotification,
             "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME");
 
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         final int userHandle = caller.getUserId();
         synchronized (getLockObject()) {
             checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
         }
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin;
-            synchronized (getLockObject()) {
-                enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
-                enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
-            }
-            LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+        EnforcingAdmin enforcingAdmin;
+        synchronized (getLockObject()) {
+            enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
+            enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
+        }
+        LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+                PolicyDefinition.LOCK_TASK,
+                enforcingAdmin,
+                caller.getUserId());
+        LockTaskPolicy policy;
+        if (currentPolicy == null) {
+            policy = new LockTaskPolicy(flags);
+        } else {
+            policy = new LockTaskPolicy(currentPolicy);
+            policy.setFlags(flags);
+        }
+        if (policy.getPackages().isEmpty()
+                && policy.getFlags() == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
+            mDevicePolicyEngine.removeLocalPolicy(
                     PolicyDefinition.LOCK_TASK,
                     enforcingAdmin,
                     caller.getUserId());
-            LockTaskPolicy policy;
-            if (currentPolicy == null) {
-                policy = new LockTaskPolicy(flags);
-            } else {
-                policy = new LockTaskPolicy(currentPolicy);
-                policy.setFlags(flags);
-            }
-            if (policy.getPackages().isEmpty()
-                    && policy.getFlags() == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
-                mDevicePolicyEngine.removeLocalPolicy(
-                        PolicyDefinition.LOCK_TASK,
-                        enforcingAdmin,
-                        caller.getUserId());
-            } else {
-                mDevicePolicyEngine.setLocalPolicy(
-                        PolicyDefinition.LOCK_TASK,
-                        enforcingAdmin,
-                        policy,
-                        caller.getUserId());
-            }
         } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            synchronized (getLockObject()) {
-                enforceCanCallLockTaskLocked(caller);
-                enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
-                setLockTaskFeaturesLocked(userHandle, flags);
-            }
+            mDevicePolicyEngine.setLocalPolicy(
+                    PolicyDefinition.LOCK_TASK,
+                    enforcingAdmin,
+                    policy,
+                    caller.getUserId());
         }
     }
 
@@ -15081,33 +14392,20 @@
 
     @Override
     public int getLockTaskFeatures(ComponentName who, String callerPackageName) {
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         final int userHandle = caller.getUserId();
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            synchronized (getLockObject()) {
-                enforceCanQueryLockTaskLocked(who, caller.getPackageName());
-            }
-            LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.LOCK_TASK, userHandle);
-            if (policy == null) {
-                // We default on the power button menu, in order to be consistent with pre-P
-                // behaviour.
-                return DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
-            }
-            return policy.getFlags();
-        } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            synchronized (getLockObject()) {
-                enforceCanCallLockTaskLocked(caller);
-                return getUserData(userHandle).mLockTaskFeatures;
-            }
+        synchronized (getLockObject()) {
+            enforceCanQueryLockTaskLocked(who, caller.getPackageName());
         }
+        LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.LOCK_TASK, userHandle);
+        if (policy == null) {
+            // We default on the power button menu, in order to be consistent with pre-P
+            // behaviour.
+            return DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+        }
+        return policy.getFlags();
     }
 
     private void maybeClearLockTaskPolicyLocked() {
@@ -15118,34 +14416,14 @@
                 if (canDPCManagedUserUseLockTaskLocked(userId)) {
                     continue;
                 }
-
-                if (isPolicyEngineForFinanceFlagEnabled()) {
-                    Map<EnforcingAdmin, PolicyValue<LockTaskPolicy>> policies =
-                            mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
-                                    PolicyDefinition.LOCK_TASK, userId);
-                    Set<EnforcingAdmin> admins = new HashSet<>(policies.keySet());
-                    for (EnforcingAdmin admin : admins) {
-                        if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
-                            mDevicePolicyEngine.removeLocalPolicy(
-                                    PolicyDefinition.LOCK_TASK, admin, userId);
-                        }
-                    }
-                } else {
-                    final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages;
-                    // TODO(b/278438525): handle in the policy engine
-                    if (!lockTaskPackages.isEmpty()) {
-                        Slogf.d(LOG_TAG,
-                                "User id " + userId
-                                        + " not affiliated. Clearing lock task packages");
-                        setLockTaskPackagesLocked(userId, Collections.<String>emptyList());
-                    }
-                    final int lockTaskFeatures = getUserData(userId).mLockTaskFeatures;
-                    if (lockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
-                        Slogf.d(LOG_TAG,
-                                "User id " + userId
-                                        + " not affiliated. Clearing lock task features");
-                        setLockTaskFeaturesLocked(userId,
-                                DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
+                Map<EnforcingAdmin, PolicyValue<LockTaskPolicy>> policies =
+                        mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+                                PolicyDefinition.LOCK_TASK, userId);
+                Set<EnforcingAdmin> admins = new HashSet<>(policies.keySet());
+                for (EnforcingAdmin admin : admins) {
+                    if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
+                        mDevicePolicyEngine.removeLocalPolicy(
+                                PolicyDefinition.LOCK_TASK, admin, userId);
                     }
                 }
             }
@@ -16442,69 +15720,22 @@
                 return result;
             }
         } else if (DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                Boolean value = mDevicePolicyEngine.getResolvedPolicy(
-                        PolicyDefinition.SCREEN_CAPTURE_DISABLED, userId);
-                if (value != null && value) {
-                    result = new Bundle();
-                    result.putInt(Intent.EXTRA_USER_ID, userId);
-                    return result;
-                }
-            } else {
-                synchronized (getLockObject()) {
-                    final DevicePolicyData policy = getUserData(userId);
-                    final int N = policy.mAdminList.size();
-                    for (int i = 0; i < N; i++) {
-                        final ActiveAdmin admin = policy.mAdminList.get(i);
-                        if (admin.disableScreenCapture) {
-                            result = new Bundle();
-                            result.putInt(Intent.EXTRA_USER_ID, userId);
-                            result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
-                                    admin.info.getComponent());
-                            return result;
-                        }
-                    }
-                }
+            Boolean value = mDevicePolicyEngine.getResolvedPolicy(
+                    PolicyDefinition.SCREEN_CAPTURE_DISABLED, userId);
+            if (value != null && value) {
+                result = new Bundle();
+                result.putInt(Intent.EXTRA_USER_ID, userId);
+                return result;
             }
         } else if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                PolicyDefinition<Boolean> policyDefinition =
-                        PolicyDefinition.getPolicyDefinitionForUserRestriction(
-                                UserManager.DISALLOW_CAMERA);
-                Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
-                if (value != null && value) {
-                    result = new Bundle();
-                    result.putInt(Intent.EXTRA_USER_ID, userId);
-                    return result;
-                }
-            } else {
-                synchronized (getLockObject()) {
-                    final DevicePolicyData policy = getUserData(userId);
-                    final int N = policy.mAdminList.size();
-                    for (int i = 0; i < N; i++) {
-                        final ActiveAdmin admin = policy.mAdminList.get(i);
-                        if (admin.disableCamera) {
-                            result = new Bundle();
-                            result.putInt(Intent.EXTRA_USER_ID, userId);
-                            result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
-                                    admin.info.getComponent());
-                            return result;
-                        }
-                    }
-                    // For the camera, a device owner on a different user can disable it globally,
-                    // so we need an additional check.
-                    if (result == null
-                            && DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
-                        final ActiveAdmin admin = getDeviceOwnerAdminLocked();
-                        if (admin != null && admin.disableCamera) {
-                            result = new Bundle();
-                            result.putInt(Intent.EXTRA_USER_ID, mOwners.getDeviceOwnerUserId());
-                            result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
-                                    admin.info.getComponent());
-                            return result;
-                        }
-                    }
-                }
+            PolicyDefinition<Boolean> policyDefinition =
+                    PolicyDefinition.getPolicyDefinitionForUserRestriction(
+                            UserManager.DISALLOW_CAMERA);
+            Boolean value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
+            if (value != null && value) {
+                result = new Bundle();
+                result.putInt(Intent.EXTRA_USER_ID, userId);
+                return result;
             }
         } else {
             long ident = mInjector.binderClearCallingIdentity();
@@ -18564,14 +17795,9 @@
             Slogf.d(LOG_TAG, "Current state of DevicePolicyData#mRemovingAdmins for user "
                     + userHandle + ": " + policy.mRemovingAdmins);
 
-            pushScreenCapturePolicy(userHandle);
-
             Slogf.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle);
         }
         pushMeteredDisabledPackages(userHandle);
-        // The removed admin might have disabled camera, so update user
-        // restrictions.
-        pushUserRestrictions(userHandle);
     }
 
     @Override
@@ -20483,20 +19709,13 @@
     }
 
     private boolean isLockTaskFeatureEnabled(int lockTaskFeature) throws RemoteException {
-        int lockTaskFeatures = 0;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.LOCK_TASK, getCurrentForegroundUserId());
-            lockTaskFeatures = policy == null
-                    // We default on the power button menu, in order to be consistent with pre-P
-                    // behaviour.
-                    ? DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
-                    : policy.getFlags();
-        } else {
-            //TODO(b/175285301): Explicitly get the user's identity to check.
-            lockTaskFeatures =
-                    getUserData(getCurrentForegroundUserId()).mLockTaskFeatures;
-        }
+        LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.LOCK_TASK, getCurrentForegroundUserId());
+        int lockTaskFeatures = policy == null
+                // We default on the power button menu, in order to be consistent with pre-P
+                // behaviour.
+                ? DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
+                : policy.getFlags();
         return (lockTaskFeatures & lockTaskFeature) == lockTaskFeature;
     }
 
@@ -20680,41 +19899,22 @@
     public void setUserControlDisabledPackages(ComponentName who, String callerPackageName,
             List<String> packages) {
         Objects.requireNonNull(packages, "packages is null");
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         checkCanExecuteOrThrowUnsafe(
                 DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                    who,
-                    MANAGE_DEVICE_POLICY_APPS_CONTROL,
-                    caller.getPackageName(),
-                    caller.getUserId());
-            Binder.withCleanCallingIdentity(() -> {
-                if (packages.isEmpty()) {
-                    removeUserControlDisabledPackages(caller, enforcingAdmin);
-                } else {
-                    addUserControlDisabledPackages(caller, enforcingAdmin, new HashSet<>(packages));
-                }
-            });
-        } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
-                    || isProfileOwner(caller) || isFinancedDeviceOwner(caller));
-            synchronized (getLockObject()) {
-                ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId());
-                if (!Objects.equals(admin.protectedPackages, packages)) {
-                    admin.protectedPackages = packages.isEmpty() ? null : packages;
-                    saveSettingsLocked(caller.getUserId());
-                    pushUserControlDisabledPackagesLocked(caller.getUserId());
-                }
+        EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                who,
+                MANAGE_DEVICE_POLICY_APPS_CONTROL,
+                caller.getPackageName(),
+                caller.getUserId());
+        Binder.withCleanCallingIdentity(() -> {
+            if (packages.isEmpty()) {
+                removeUserControlDisabledPackages(caller, enforcingAdmin);
+            } else {
+                addUserControlDisabledPackages(caller, enforcingAdmin, new HashSet<>(packages));
             }
-        }
+        });
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES)
@@ -20756,34 +19956,17 @@
     @Override
     public List<String> getUserControlDisabledPackages(ComponentName who,
             String callerPackageName) {
-        CallerIdentity caller;
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            caller = getCallerIdentity(who, callerPackageName);
-        } else {
-            caller = getCallerIdentity(who);
-        }
-
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            enforceCanQuery(
-                    MANAGE_DEVICE_POLICY_APPS_CONTROL,
-                    caller.getPackageName(),
-                    caller.getUserId());
-            // This retrieves the policy for the calling user only, DOs for example can't know
-            // what's enforced globally or on another user.
-            Set<String> packages = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
-                    caller.getUserId());
-            return packages == null ? Collections.emptyList() : packages.stream().toList();
-        } else {
-            Objects.requireNonNull(who, "ComponentName is null");
-            Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
-                    || isProfileOwner(caller) || isFinancedDeviceOwner(caller));
-            synchronized (getLockObject()) {
-                ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId());
-                return admin.protectedPackages != null
-                        ? admin.protectedPackages : Collections.emptyList();
-            }
-        }
+        CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+        enforceCanQuery(
+                MANAGE_DEVICE_POLICY_APPS_CONTROL,
+                caller.getPackageName(),
+                caller.getUserId());
+        // This retrieves the policy for the calling user only, DOs for example can't know
+        // what's enforced globally or on another user.
+        Set<String> packages = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
+                caller.getUserId());
+        return packages == null ? Collections.emptyList() : packages.stream().toList();
     }
 
     @Override
@@ -21066,27 +20249,18 @@
         Slogf.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending",
                 parentUserId);
 
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            // TODO(b/280602237): migrate properly
-            ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
-            if (profileOwner != null) {
-                EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
-                        profileOwner.info.getComponent(),
-                        profileUserId,
-                        profileOwner);
-                mDevicePolicyEngine.setLocalPolicy(
-                        PolicyDefinition.PERSONAL_APPS_SUSPENDED,
-                        admin,
-                        new BooleanPolicyValue(suspended),
-                        parentUserId);
-            }
-        } else {
-            if (suspended) {
-                suspendPersonalAppsInPackageManager(parentUserId);
-            } else {
-                mInjector.getPackageManagerInternal().unsuspendForSuspendingPackage(
-                        PLATFORM_PACKAGE_NAME, parentUserId);
-            }
+        // TODO(b/280602237): migrate properly
+        ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
+        if (profileOwner != null) {
+            EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                    profileOwner.info.getComponent(),
+                    profileUserId,
+                    profileOwner);
+            mDevicePolicyEngine.setLocalPolicy(
+                    PolicyDefinition.PERSONAL_APPS_SUSPENDED,
+                    admin,
+                    new BooleanPolicyValue(suspended),
+                    parentUserId);
         }
 
         synchronized (getLockObject()) {
@@ -22373,35 +21547,18 @@
     public void setUsbDataSignalingEnabled(String packageName, boolean enabled) {
         Objects.requireNonNull(packageName, "Admin package name must be provided");
         final CallerIdentity caller = getCallerIdentity(packageName);
-        if (!isPolicyEngineForFinanceFlagEnabled()) {
-            Preconditions.checkCallAuthorization(
-                    isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
-                    "USB data signaling can only be controlled by a device owner or "
-                            + "a profile owner on an organization-owned device.");
-            Preconditions.checkState(canUsbDataSignalingBeDisabled(),
-                    "USB data signaling cannot be disabled.");
-        }
 
         synchronized (getLockObject()) {
-            if (isPolicyEngineForFinanceFlagEnabled()) {
-                EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                        /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
-                        caller.getPackageName(),
-                        caller.getUserId());
-                Preconditions.checkState(canUsbDataSignalingBeDisabled(),
-                        "USB data signaling cannot be disabled.");
-                mDevicePolicyEngine.setGlobalPolicy(
-                        PolicyDefinition.USB_DATA_SIGNALING,
-                        enforcingAdmin,
-                        new BooleanPolicyValue(enabled));
-            } else {
-                ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
-                if (admin.mUsbDataSignalingEnabled != enabled) {
-                    admin.mUsbDataSignalingEnabled = enabled;
-                    saveSettingsLocked(caller.getUserId());
-                    updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
-                }
-            }
+            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                    /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
+                    caller.getPackageName(),
+                    caller.getUserId());
+            Preconditions.checkState(canUsbDataSignalingBeDisabled(),
+                    "USB data signaling cannot be disabled.");
+            mDevicePolicyEngine.setGlobalPolicy(
+                    PolicyDefinition.USB_DATA_SIGNALING,
+                    enforcingAdmin,
+                    new BooleanPolicyValue(enabled));
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING)
@@ -22423,24 +21580,10 @@
     @Override
     public boolean isUsbDataSignalingEnabled(String packageName) {
         final CallerIdentity caller = getCallerIdentity(packageName);
-        if (isPolicyEngineForFinanceFlagEnabled()) {
-            Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.USB_DATA_SIGNALING,
-                    caller.getUserId());
-            return enabled == null || enabled;
-        } else {
-            synchronized (getLockObject()) {
-                // If the caller is an admin, return the policy set by itself. Otherwise
-                // return the device-wide policy.
-                if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(
-                        caller)) {
-                    return getProfileOwnerOrDeviceOwnerLocked(
-                            caller.getUserId()).mUsbDataSignalingEnabled;
-                } else {
-                    return isUsbDataSignalingEnabledInternalLocked();
-                }
-            }
-        }
+        Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.USB_DATA_SIGNALING,
+                caller.getUserId());
+        return enabled == null || enabled;
     }
 
     private boolean isUsbDataSignalingEnabledInternalLocked() {
@@ -22849,9 +21992,6 @@
         }
 
         private void handleFinancedDeviceKioskRoleChange() {
-            if (!isPolicyEngineForFinanceFlagEnabled()) {
-                return;
-            }
             Slog.i(LOG_TAG, "Handling action " + ACTION_DEVICE_FINANCING_STATE_CHANGED);
             Intent intent = new Intent(ACTION_DEVICE_FINANCING_STATE_CHANGED);
             mInjector.binderWithCleanCallingIdentity(() -> {
@@ -23842,13 +22982,6 @@
                 DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
     }
 
-    static boolean isPolicyEngineForFinanceFlagEnabled() {
-        return DeviceConfig.getBoolean(
-                NAMESPACE_DEVICE_POLICY_MANAGER,
-                ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG,
-                DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG);
-    }
-
     private static boolean isKeepProfilesRunningFlagEnabled() {
         return DeviceConfig.getBoolean(
                 NAMESPACE_DEVICE_POLICY_MANAGER,
@@ -24200,9 +23333,7 @@
     }
 
     private boolean shouldMigrateToDevicePolicyEngine() {
-        return mInjector.binderWithCleanCallingIdentity(() ->
-                (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled())
-                        && !mOwners.isMigratedToPolicyEngine());
+        return mInjector.binderWithCleanCallingIdentity(() -> !mOwners.isMigratedToPolicyEngine());
     }
 
     /**
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index d32b6be..78e5a42 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -331,6 +331,7 @@
     @Test
     public void testGetBootUser_Headless_ThrowsIfOnlySystemUserExists() throws Exception {
         setSystemUserHeadless(true);
+        removeNonSystemUsers();
 
         assertThrows(UserManager.CheckedUserOperationException.class,
                 () -> mUmi.getBootUser(/* waitUntilSet= */ false));
@@ -504,6 +505,14 @@
                 .isFalse();
     }
 
+    private void removeNonSystemUsers() {
+        for (UserInfo user : mUms.getUsers(true)) {
+            if (!user.getUserHandle().isSystem()) {
+                mUms.removeUserInfo(user.id);
+            }
+        }
+    }
+
     private void resetUserSwitcherEnabled() {
         mUms.putUserInfo(new UserInfo(USER_ID, "Test User", 0));
         mUms.setUserRestriction(DISALLOW_USER_SWITCH, false, USER_ID);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 58cdb1b..91d8ceb 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -50,9 +50,7 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.power.batterysaver.BatterySaverController;
-import com.android.server.power.batterysaver.BatterySaverPolicy;
-import com.android.server.power.batterysaver.BatterySavingStats;
+import com.android.server.power.batterysaver.BatterySaverStateMachine;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import org.junit.Before;
@@ -69,8 +67,7 @@
     private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
     private static final int USER_ID = 0;
 
-    @Mock private BatterySaverController mBatterySaverControllerMock;
-    @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
+    @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
     @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
     @Mock private Notifier mNotifierMock;
     @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@@ -263,16 +260,8 @@
         }
 
         @Override
-        BatterySaverPolicy createBatterySaverPolicy(
-                Object lock, Context context, BatterySavingStats batterySavingStats) {
-            return mBatterySaverPolicyMock;
-        }
-
-        @Override
-        BatterySaverController createBatterySaverController(
-                Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
-                BatterySavingStats batterySavingStats) {
-            return mBatterySaverControllerMock;
+        BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) {
+            return mBatterySaverStateMachineMock;
         }
 
         @Override
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index d6d5264..8e1d8ab 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -108,7 +108,6 @@
 import com.android.server.power.batterysaver.BatterySaverController;
 import com.android.server.power.batterysaver.BatterySaverPolicy;
 import com.android.server.power.batterysaver.BatterySaverStateMachine;
-import com.android.server.power.batterysaver.BatterySavingStats;
 import com.android.server.testutils.OffsettableClock;
 
 import com.google.testing.junit.testparameterinjector.TestParameter;
@@ -184,6 +183,7 @@
     private OffsettableClock mClock;
     private long mLastElapsedRealtime;
     private TestLooper mTestLooper;
+    private boolean mIsBatterySaverSupported = true;
 
     private static class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
         private final IntentFilter mFilter;
@@ -215,6 +215,10 @@
                 .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
                 .setBrightnessFactor(BRIGHTNESS_FACTOR)
                 .build();
+        when(mBatterySaverStateMachineMock.getBatterySaverController()).thenReturn(
+                mBatterySaverControllerMock);
+        when(mBatterySaverStateMachineMock.getBatterySaverPolicy()).thenReturn(
+                mBatterySaverPolicyMock);
         when(mBatterySaverPolicyMock.getBatterySaverPolicy(
                 eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
                 .thenReturn(powerSaveState);
@@ -235,6 +239,7 @@
         mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+        setBatterySaverSupported();
 
         MockContentResolver cr = new MockContentResolver(mContextSpy);
         cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -269,21 +274,7 @@
             }
 
             @Override
-            BatterySaverPolicy createBatterySaverPolicy(
-                    Object lock, Context context, BatterySavingStats batterySavingStats) {
-                return mBatterySaverPolicyMock;
-            }
-
-            @Override
-            BatterySaverController createBatterySaverController(
-                    Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
-                    BatterySavingStats batterySavingStats) {
-                return mBatterySaverControllerMock;
-            }
-
-            @Override
-            BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
-                    BatterySaverController batterySaverController) {
+            BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) {
                 return mBatterySaverStateMachineMock;
             }
 
@@ -480,6 +471,12 @@
         mTestLooper.dispatchAll();
     }
 
+    private void setBatterySaverSupported() {
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_batterySaverSupported)).thenReturn(
+                mIsBatterySaverSupported);
+    }
+
     @Test
     public void testCreateService_initializesNativeServiceAndSetsPowerModes() {
         PowerManagerService service = createService();
@@ -2543,6 +2540,19 @@
     }
 
     @Test
+    public void testGetFullPowerSavePolicy_whenNoBatterySaverSupported() {
+        mIsBatterySaverSupported = false;
+        setBatterySaverSupported();
+        createService();
+        BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
+        assertFalse(mService.getBinderServiceInstance().setPowerSaveModeEnabled(true));
+        BatterySaverPolicyConfig policyConfig =
+                mService.getBinderServiceInstance().getFullPowerSavePolicy();
+        assertThat(mockReturnConfig.toString()).isEqualTo(policyConfig.toString());
+        verify(mBatterySaverStateMachineMock, never()).getFullBatterySaverPolicy();
+    }
+
+    @Test
     public void testSetFullPowerSavePolicy_callsStateMachine() {
         createService();
         startSystem();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 48ba765..f2cbef6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -38,7 +38,6 @@
 
 import com.android.internal.os.BatteryStatsHistory;
 import com.android.internal.os.BatteryStatsHistoryIterator;
-import com.android.internal.os.Clock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -65,7 +64,7 @@
     private final Parcel mHistoryBuffer = Parcel.obtain();
     private File mSystemDir;
     private File mHistoryDir;
-    private final Clock mClock = new MockClock();
+    private final MockClock mClock = new MockClock();
     private BatteryStatsHistory mHistory;
     private BatteryStats.HistoryPrinter mHistoryPrinter;
     @Mock
@@ -486,10 +485,94 @@
                 NetworkRegistrationInfo.NR_STATE_NONE);
     }
 
+    @Test
+    public void largeTagPool() {
+        // Keep the preserved part of history short - we only need to capture the very tail of
+        // history.
+        mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 1, 6000,
+                mStepDetailsCalculator, mClock, mTracer);
+
+        mHistory.forceRecordAllHistory();
+
+        mClock.realtime = 2_000_000;
+        mClock.uptime = 1_000_000;
+        // More than 32k strings
+        final int tagCount = 0x7FFF + 20;
+        for (int tag = 0; tag < tagCount;) {
+            mClock.realtime += 10;
+            mClock.uptime += 10;
+            mHistory.recordEvent(mClock.realtime, mClock.uptime, HistoryItem.EVENT_ALARM_START,
+                    "a" + (tag++), 42);
+
+            mHistory.setBatteryState(true, BatteryManager.BATTERY_STATUS_CHARGING, tag % 50, 0);
+            mClock.realtime += 10;
+            mClock.uptime += 10;
+            mHistory.recordWakelockStartEvent(mClock.realtime, mClock.uptime, "w" + tag, 42);
+            mClock.realtime += 10;
+            mClock.uptime += 10;
+            mHistory.recordWakelockStopEvent(mClock.realtime, mClock.uptime, "w" + tag, 42);
+            tag++;
+
+            mHistory.recordWakeupEvent(mClock.realtime, mClock.uptime, "wr" + (tag++));
+        }
+
+        int eventTagsPooled = 0;
+        int eventTagsUnpooled = 0;
+        int wakelockTagsPooled = 0;
+        int wakelockTagsUnpooled = 0;
+        int wakeReasonTagsPooled = 0;
+        int wakeReasonTagsUnpooled = 0;
+        for (BatteryStatsHistoryIterator iterator = mHistory.iterate(); iterator.hasNext(); ) {
+            HistoryItem item  = iterator.next();
+            if (item.cmd != HistoryItem.CMD_UPDATE) {
+                continue;
+            }
+            String checkinDump = toString(item, true);
+            if (item.eventCode == HistoryItem.EVENT_ALARM_START) {
+                if (item.eventTag.poolIdx != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+                    eventTagsPooled++;
+                    assertThat(checkinDump).contains("+Eal=" + item.eventTag.poolIdx);
+                } else {
+                    eventTagsUnpooled++;
+                    assertThat(checkinDump).contains("+Eal=42:\"" + item.eventTag.string + "\"");
+                }
+            }
+
+            if (item.wakelockTag != null) {
+                if (item.wakelockTag.poolIdx != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+                    wakelockTagsPooled++;
+                    assertThat(checkinDump).contains("w=" + item.wakelockTag.poolIdx);
+                } else {
+                    wakelockTagsUnpooled++;
+                    assertThat(checkinDump).contains("w=42:\"" + item.wakelockTag.string + "\"");
+                }
+            }
+
+            if (item.wakeReasonTag != null) {
+                if (item.wakeReasonTag.poolIdx
+                        != BatteryStats.HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
+                    wakeReasonTagsPooled++;
+                    assertThat(checkinDump).contains("wr=" + item.wakeReasonTag.poolIdx);
+                } else {
+                    wakeReasonTagsUnpooled++;
+                    assertThat(checkinDump).contains("wr=0:\"" + item.wakeReasonTag.string + "\"");
+                }
+            }
+        }
+
+        // Self-check - ensure that we have all cases represented in the test
+        assertThat(eventTagsPooled).isGreaterThan(0);
+        assertThat(eventTagsUnpooled).isGreaterThan(0);
+        assertThat(wakelockTagsPooled).isGreaterThan(0);
+        assertThat(wakelockTagsUnpooled).isGreaterThan(0);
+        assertThat(wakeReasonTagsPooled).isGreaterThan(0);
+        assertThat(wakeReasonTagsUnpooled).isGreaterThan(0);
+    }
+
     private String toString(BatteryStats.HistoryItem item, boolean checkin) {
         StringWriter writer = new StringWriter();
         PrintWriter pw = new PrintWriter(writer);
-        mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ true);
+        mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ false);
         pw.flush();
         return writer.toString();
     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 40c762c..dec89d9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -383,6 +383,47 @@
     }
 
     @Test
+    public void handleRoutingChange_toSwitchInActivePath_noStandby() {
+        int newPlaybackPhysicalAddress = 0x2100;
+        int switchPhysicalAddress = 0x2000;
+        mNativeWrapper.setPhysicalAddress(newPlaybackPhysicalAddress);
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                newPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, newPlaybackPhysicalAddress,
+                        switchPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
+        assertThat(mPowerManager.isInteractive()).isTrue();
+    }
+
+    @Test
+    public void handleRoutingChange_toTv_StandbyNow() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, mPlaybackPhysicalAddress,
+                        Constants.TV_PHYSICAL_ADDRESS);
+        assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
+        assertThat(mPowerManager.isInteractive()).isFalse();
+    }
+
+    @Test
     public void handleRoutingChange_otherDevice_StandbyNow_InactiveSource() {
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index dd681aa..cb659b6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -130,7 +130,8 @@
             // Keep system and current user
             if (user.id != UserHandle.USER_SYSTEM &&
                     user.id != currentUser &&
-                    user.id != communalProfileId) {
+                    user.id != communalProfileId &&
+                    !user.isMain()) {
                 removeUser(user.id);
             }
         }
@@ -325,6 +326,24 @@
         assertThat(hasUser(user2.id)).isTrue();
     }
 
+
+    @MediumTest
+    @Test
+    public void testGetFullUserCount() throws Exception {
+        assertThat(mUserManager.getFullUserCount()).isEqualTo(1);
+        UserInfo user1 = createUser("User 1", UserInfo.FLAG_FULL);
+        UserInfo user2 = createUser("User 2", UserInfo.FLAG_ADMIN);
+
+        assertThat(user1).isNotNull();
+        assertThat(user2).isNotNull();
+
+        assertThat(mUserManager.getFullUserCount()).isEqualTo(3);
+        removeUser(user1.id);
+        assertThat(mUserManager.getFullUserCount()).isEqualTo(2);
+        removeUser(user2.id);
+        assertThat(mUserManager.getFullUserCount()).isEqualTo(1);
+    }
+
     /**
      * Tests that UserManager knows how many users can be created.
      *
@@ -1105,16 +1124,16 @@
     public void testCreateProfileForUser_disallowAddManagedProfile() throws Exception {
         assumeManagedUsersSupported();
         final int mainUserId = mUserManager.getMainUser().getIdentifier();
-        final UserHandle mainUserHandle = asHandle(mainUserId);
+        final UserHandle currentUserHandle = asHandle(ActivityManager.getCurrentUser());
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
-                mainUserHandle);
+                currentUserHandle);
         try {
             UserInfo userInfo = createProfileForUser("Managed",
                     UserManager.USER_TYPE_PROFILE_MANAGED, mainUserId);
             assertThat(userInfo).isNull();
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
-                    mainUserHandle);
+                    currentUserHandle);
         }
     }
 
@@ -1190,6 +1209,7 @@
     @Test
     public void testGetManagedProfileCreationTime() throws Exception {
         assumeManagedUsersSupported();
+        assumeTrue("User does not have access to creation time", mUserManager.isMainUser());
         final int mainUserId = mUserManager.getMainUser().getIdentifier();
         final long startTime = System.currentTimeMillis();
         UserInfo profile = createProfileForUser("Managed 1",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 3eed0b7..302ad7f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2719,6 +2719,9 @@
         assertEquals(Configuration.ORIENTATION_PORTRAIT, displayConfig.orientation);
         assertEquals(Configuration.ORIENTATION_PORTRAIT, activityConfig.orientation);
 
+        // Unblock the rotation animation, so the further orientation updates won't be ignored.
+        unblockDisplayRotation(activity.mDisplayContent);
+
         final ActivityRecord topActivity = createActivityRecord(activity.getTask());
         topActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 4290f4b..d169a58 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -490,6 +490,11 @@
                 .build();
         final Task task = activity.getTask();
         final TaskDisplayArea tda = task.getDisplayArea();
+        // Ensure the display is not a large screen
+        if (tda.getConfiguration().smallestScreenWidthDp
+                >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP) {
+            resizeDisplay(activity.mDisplayContent, 500, 800);
+        }
 
         // Ignore the activity min width/height for determine multi window eligibility.
         mAtm.mRespectsActivityMinWidthHeightMultiWindow = -1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 41c0caae..c84eab3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -36,6 +36,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 
 import android.app.WindowConfiguration;
@@ -507,6 +508,7 @@
         // Return the default display as the value to mirror to ensure the VD with flag mirroring
         // creates a ContentRecordingSession automatically.
         doReturn(DEFAULT_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
+        clearInvocations(virtualDisplay);
         virtualDisplay.updateRecording();
 
         // THEN mirroring is initiated for the default display's DisplayArea.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
index f536cd0..87dbca5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -114,8 +114,9 @@
 
     @Test
     public void testResolveOverrideConfiguration_reverseOrientationWhenDifferentFromParentRoot() {
-        mDisplayContent.setBounds(0, 0, 600, 900);
-        mDisplayContent.updateOrientation();
+        // Rotate the display to portrait.
+        final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+        displayRotation.setRotation(displayRotation.getPortraitRotation());
         mDisplayContent.sendNewConfiguration();
 
         // DAG fills Display
@@ -128,7 +129,7 @@
         assertThat(mDisplayAreaGroup.getConfiguration().orientation)
                 .isEqualTo(ORIENTATION_LANDSCAPE);
 
-        // DisplayAreaGroup is portriat, same as Display
+        // DisplayAreaGroup is portrait, same as Display
         mDisplayAreaGroup.setBounds(0, 0, 300, 450);
 
         assertThat(mDisplayAreaGroup.getConfiguration().orientation)
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1c0fd4f..a2b7da3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1819,7 +1819,7 @@
 
         final ActivityRecord activity = createActivityRecord(mDisplayContent);
         final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent);
-        recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
         doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController();
 
         // Do not rotate if the recents animation is animating on top.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 6e52af1..c1be5ca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -353,6 +353,7 @@
 
     @Test
     public void testRecentViewInFixedPortraitWhenTopAppInLandscape() {
+        makeDisplayPortrait(mDefaultDisplay);
         unblockDisplayRotation(mDefaultDisplay);
         mWm.setRecentsAnimationController(mController);
 
@@ -488,6 +489,7 @@
 
     @Test
     public void testWallpaperHasFixedRotationApplied() {
+        makeDisplayPortrait(mDefaultDisplay);
         unblockDisplayRotation(mDefaultDisplay);
         mWm.setRecentsAnimationController(mController);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index be436bf..7634d9f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -384,7 +384,16 @@
     }
 
     private void tearDown() {
-        mWmService.mRoot.forAllDisplayPolicies(DisplayPolicy::release);
+        for (int i = mWmService.mRoot.getChildCount() - 1; i >= 0; i--) {
+            final DisplayContent dc = mWmService.mRoot.getChildAt(i);
+            // Unregister SettingsObserver.
+            dc.getDisplayPolicy().release();
+            // Unregister SensorEventListener (foldable device may register for hinge angle).
+            dc.getDisplayRotation().onDisplayRemoved();
+            if (dc.mDisplayRotationCompatPolicy != null) {
+                dc.mDisplayRotationCompatPolicy.dispose();
+            }
+        }
 
         // Unregister display listener from root to avoid issues with subsequent tests.
         mContext.getSystemService(DisplayManager.class)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 54b9351..bfa279d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -143,6 +143,7 @@
     @Before
     public void setup() throws RemoteException {
         MockitoAnnotations.initMocks(this);
+        removeGlobalMinSizeRestriction();
         mWindowOrganizerController = mAtm.mWindowOrganizerController;
         mTransitionController = mWindowOrganizerController.mTransitionController;
         mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 6305bb6..6216acb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -116,10 +116,7 @@
     public void testWallpaperSizeWithFixedTransform() {
         // No wallpaper
         final DisplayContent dc = mDisplayContent;
-        if (dc.mBaseDisplayHeight == dc.mBaseDisplayWidth) {
-            // Make sure the size is different when changing orientation.
-            resizeDisplay(dc, 500, 1000);
-        }
+        makeDisplayPortrait(dc);
 
         // No wallpaper WSA Surface
         final WindowState wallpaperWindow = createWallpaperWindow(dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 55fda05..76576f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -107,6 +107,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
+import java.util.ArrayList;
+
 /**
  * Build/Install/Run:
  * atest WmTests:WindowManagerServiceTests
@@ -975,6 +977,28 @@
         verify(window, times(2)).requestAppKeyboardShortcuts(receiver, 0);
     }
 
+    @Test
+    public void testReportSystemGestureExclusionChanged_invalidWindow() {
+        final Session session = mock(Session.class);
+        final IWindow window = mock(IWindow.class);
+        final IBinder binder = mock(IBinder.class);
+        doReturn(binder).when(window).asBinder();
+
+        // No exception even if the window doesn't exist
+        mWm.reportSystemGestureExclusionChanged(session, window, new ArrayList<>());
+    }
+
+    @Test
+    public void testReportKeepClearAreasChanged_invalidWindow() {
+        final Session session = mock(Session.class);
+        final IWindow window = mock(IWindow.class);
+        final IBinder binder = mock(IBinder.class);
+        doReturn(binder).when(window).asBinder();
+
+        // No exception even if the window doesn't exist
+        mWm.reportKeepClearAreasChanged(session, window, new ArrayList<>(), new ArrayList<>());
+    }
+
     class TestResultReceiver implements IResultReceiver {
         public android.os.Bundle resultData;
         private final IBinder mBinder = mock(IBinder.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 62de67a..ae7b161 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -963,6 +963,8 @@
      * @see DisplayRotation#updateRotationUnchecked
      */
     void unblockDisplayRotation(DisplayContent dc) {
+        dc.mOpeningApps.clear();
+        mWm.mAppsFreezingScreen = 0;
         mWm.stopFreezingDisplayLocked();
         // The rotation animation won't actually play, it needs to be cleared manually.
         dc.setRotationAnimation(null);
@@ -971,11 +973,19 @@
     static void resizeDisplay(DisplayContent displayContent, int width, int height) {
         displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity,
                 displayContent.mBaseDisplayPhysicalXDpi, displayContent.mBaseDisplayPhysicalYDpi);
+        displayContent.getDisplayRotation().configure(width, height);
         final Configuration c = new Configuration();
         displayContent.computeScreenConfiguration(c);
         displayContent.onRequestedOverrideConfigurationChanged(c);
     }
 
+    /** Used for the tests that assume the display is portrait by default. */
+    static void makeDisplayPortrait(DisplayContent displayContent) {
+        if (displayContent.mBaseDisplayHeight <= displayContent.mBaseDisplayWidth) {
+            resizeDisplay(displayContent, 500, 1000);
+        }
+    }
+
     // The window definition for UseTestDisplay#addWindows. The test can declare to add only
     // necessary windows, that avoids adding unnecessary overhead of unused windows.
     static final int W_NOTIFICATION_SHADE = TYPE_NOTIFICATION_SHADE;
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 3e67286..2ccc0fa 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -39,7 +39,11 @@
         "src/**/activityembedding/*.kt",
         "src/**/activityembedding/open/*.kt",
         "src/**/activityembedding/close/*.kt",
+        "src/**/activityembedding/layoutchange/*.kt",
+        "src/**/activityembedding/pip/*.kt",
         "src/**/activityembedding/rotation/*.kt",
+        "src/**/activityembedding/rtl/*.kt",
+        "src/**/activityembedding/splitscreen/*.kt",
     ],
 }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
index 4530ef3..0c36c59 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
@@ -38,7 +38,7 @@
  * Setup: Launch A|B in split with B being the secondary activity. Transitions: Finish B and expect
  * A to become fullscreen.
  *
- * To run this test: `atest FlickerTests:CloseSecondaryActivityInSplitTest`
+ * To run this test: `atest FlickerTestsOther:CloseSecondaryActivityInSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
index baf109b..adff579 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.flicker.activityembedding
+package com.android.server.wm.flicker.activityembedding.layoutchange
 
 import android.platform.test.annotations.Presubmit
 import android.tools.common.datatypes.Rect
@@ -23,8 +23,9 @@
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import androidx.test.filters.RequiresDevice
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,7 +39,7 @@
  * windows are equal in size. B is on the top and A is on the bottom. Transitions: Change the split
  * ratio to A:B=0.7:0.3, expect bounds change for both A and B.
  *
- * To run this test: `atest FlickerTests:HorizontalSplitChangeRatioTest`
+ * To run this test: `atest FlickerTestsOther:HorizontalSplitChangeRatioTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index d97027e..ce9c337 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -39,7 +39,7 @@
  * Setup: Launch A|B in split with B being the secondary activity. Transitions: A start C with
  * alwaysExpand=true, expect C to launch in fullscreen and cover split A|B.
  *
- * To run this test: `atest FlickerTests:MainActivityStartsSecondaryWithAlwaysExpandTest`
+ * To run this test: `atest FlickerTestsOther:MainActivityStartsSecondaryWithAlwaysExpandTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
index 8a997dd..48edf6d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
@@ -34,7 +34,7 @@
  * Test opening an activity that will launch another activity as ActivityEmbedding placeholder in
  * split.
  *
- * To run this test: `atest FlickerTests:OpenActivityEmbeddingPlaceholderSplitTest`
+ * To run this test: `atest FlickerTestsOther:OpenActivityEmbeddingPlaceholderSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
index 49aa84b..3657820 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
@@ -34,7 +34,7 @@
 /**
  * Test opening a secondary activity that will split with the main activity.
  *
- * To run this test: `atest FlickerTests:OpenActivityEmbeddingSecondaryToSplitTest`
+ * To run this test: `atest FlickerTestsOther:OpenActivityEmbeddingSecondaryToSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
index 4bb2246..9f9fc23 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
@@ -39,7 +39,7 @@
  *
  * Transitions: Let B start C, expect C to cover B and end up in split A|C.
  *
- * To run this test: `atest FlickerTests:OpenThirdActivityOverSplitTest`
+ * To run this test: `atest FlickerTestsOther:OpenThirdActivityOverSplitTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index 0fdf63c..30e833f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -41,7 +41,7 @@
  * Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and finishes
  * itself, end up in split A|B.
  *
- * To run this test: `atest FlickerTests:OpenTrampolineActivityTest`
+ * To run this test: `atest FlickerTestsOther:OpenTrampolineActivityTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index e4c35b2..359845d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.flicker.activityembedding
+package com.android.server.wm.flicker.activityembedding.pip
 
 import android.platform.test.annotations.Presubmit
 import android.tools.common.datatypes.Rect
@@ -25,6 +25,7 @@
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
 import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -38,7 +39,7 @@
  * Setup: Start from a split A|B. Transition: B enters PIP, observe the window shrink to the bottom
  * right corner on screen.
  *
- * To run this test: `atest FlickerTests:SecondaryActivityEnterPipTest`
+ * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
index da56500..4f7d8a4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
@@ -23,6 +23,7 @@
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
 import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
 import com.android.server.wm.flicker.rotation.RotationTransition
 import org.junit.FixMethodOrder
@@ -37,7 +38,7 @@
  * Setup: Launch A|B in split with B being the secondary activity. Transitions: Rotate display, and
  * expect A and B to split evenly in new rotation.
  *
- * To run this test: `atest FlickerTests:RotateSplitNoChangeTest`
+ * To run this test: `atest FlickerTestsOther:RotateSplitNoChangeTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
index 4bc17ed..6be78f8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.flicker.activityembedding
+package com.android.server.wm.flicker.activityembedding.rtl
 
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -22,6 +22,7 @@
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
 import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -36,7 +37,7 @@
  * PlaceholderPrimary, which is configured to launch with PlaceholderSecondary in RTL. Expect split
  * PlaceholderSecondary|PlaceholderPrimary covering split B|A.
  *
- * To run this test: `atest FlickerTests:RTLStartSecondaryWithPlaceholderTest`
+ * To run this test: `atest FlickerTestsOther:RTLStartSecondaryWithPlaceholderTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)