Merge "Add a subclass of IntrusionDetectionEventTransport that overrides the methods for a real datasource." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 0bce29f..56831d7 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -966,6 +966,13 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+java_aconfig_library {
+    name: "android.app.flags-aconfig-java-host",
+    aconfig_declarations: "android.app.flags-aconfig",
+    host_supported: true,
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Broadcast Radio
 aconfig_declarations {
     name: "android.hardware.radio.flags-aconfig",
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 94768d1..8f6a508 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -98,6 +98,7 @@
     private static final int VULKAN_1_1 = 0x00401000;
     private static final int VULKAN_1_2 = 0x00402000;
     private static final int VULKAN_1_3 = 0x00403000;
+    private static final int VULKAN_1_4 = 0x00404000;
 
     // Values for UPDATABLE_DRIVER_ALL_APPS
     // 0: Default (Invalid values fallback to default as well)
@@ -179,6 +180,10 @@
     private int getVulkanVersion(PackageManager pm) {
         // PackageManager doesn't have an API to retrieve the version of a specific feature, and we
         // need to avoid retrieving all system features here and looping through them.
+        if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_4)) {
+            return VULKAN_1_4;
+        }
+
         if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_3)) {
             return VULKAN_1_3;
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d5b5258..8d054f4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6374,6 +6374,14 @@
         public static final String LOCALE_PREFERENCES = "locale_preferences";
 
         /**
+         * User can change the region from region settings. This records user's preferred region.
+         *
+         * E.g. : if user's locale is en-US, this will record US
+         * @hide
+         */
+        public static final String PREFERRED_REGION = "preferred_region";
+
+        /**
          * Setting to enable camera flash notification feature.
          * <ul>
          *     <li> 0 = Off
@@ -6547,6 +6555,7 @@
             PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE);
             PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING);
             PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON);
+            PRIVATE_SETTINGS.add(PREFERRED_REGION);
         }
 
         /**
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index a5c837b..2007a5f 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -96,6 +96,14 @@
 }
 
 flag {
+    name: "prevent_intent_redirect_show_toast_if_nested_keys_not_collected"
+    namespace: "responsible_apis"
+    description: "Prevent intent redirect attacks by showing a toast if not yet collected"
+    bug: "361143368"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "prevent_intent_redirect_throw_exception_if_nested_keys_not_collected"
     namespace: "responsible_apis"
     description: "Prevent intent redirect attacks by throwing exception if the intent does not collect nested keys"
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 14a14e6..fba8e42 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -170,6 +170,9 @@
                 }
                 parcel.writeInt(event.mSaveDialogNotShowReason);
                 parcel.writeInt(event.mUiType);
+                if (Flags.addLastFocusedIdToFillEventHistory()) {
+                    parcel.writeParcelable(event.mFocusedId, 0);
+                }
             }
         }
     }
@@ -375,6 +378,8 @@
         @UiType
         private final int mUiType;
 
+        @Nullable private final AutofillId mFocusedId;
+
         /**
          * Returns the type of the event.
          *
@@ -388,7 +393,7 @@
         @FlaggedApi(FLAG_AUTOFILL_W_METRICS)
         @Nullable
         public AutofillId getFocusedId() {
-            return null;
+            return mFocusedId;
         }
 
         /**
@@ -624,6 +629,7 @@
          * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
          * respective entry on {@code manuallyFilledFieldIds}.
          * @param detectedFieldClassifications the field classification matches.
+         * @param focusedId the field which was focused at the time of event trigger
          *
          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
          * {@code changedDatasetIds} doesn't match.
@@ -640,11 +646,12 @@
                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
                 @Nullable AutofillId[] detectedFieldIds,
-                @Nullable FieldClassification[] detectedFieldClassifications) {
+                @Nullable FieldClassification[] detectedFieldClassifications,
+                @Nullable AutofillId focusedId) {
             this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds,
                     changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
                     manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
-                    NO_SAVE_UI_REASON_NONE);
+                    NO_SAVE_UI_REASON_NONE, focusedId);
         }
 
         /**
@@ -665,6 +672,7 @@
          * respective entry on {@code manuallyFilledFieldIds}.
          * @param detectedFieldClassifications the field classification matches.
          * @param saveDialogNotShowReason The reason why a save dialog was not shown.
+         * @param focusedId the field which was focused at the time of event trigger
          *
          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
          * {@code changedDatasetIds} doesn't match.
@@ -682,11 +690,12 @@
                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
                 @Nullable AutofillId[] detectedFieldIds,
                 @Nullable FieldClassification[] detectedFieldClassifications,
-                int saveDialogNotShowReason) {
+                int saveDialogNotShowReason,
+                @Nullable AutofillId focusedId) {
             this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds,
                     changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
                     manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
-                    saveDialogNotShowReason, UI_TYPE_UNKNOWN);
+                    saveDialogNotShowReason, UI_TYPE_UNKNOWN, focusedId);
         }
 
         /**
@@ -708,6 +717,7 @@
          * @param detectedFieldClassifications the field classification matches.
          * @param saveDialogNotShowReason The reason why a save dialog was not shown.
          * @param uiType The ui presentation type for fill suggestion.
+         * @param focusedId the field which was focused at the time of event trigger
          *
          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
          * {@code changedDatasetIds} doesn't match.
@@ -725,7 +735,7 @@
                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
                 @Nullable AutofillId[] detectedFieldIds,
                 @Nullable FieldClassification[] detectedFieldClassifications,
-                int saveDialogNotShowReason, int uiType) {
+                int saveDialogNotShowReason, int uiType, @Nullable AutofillId focusedId) {
             mEventType = Preconditions.checkArgumentInRange(eventType, 0,
                     TYPE_VIEW_REQUESTED_AUTOFILL, "eventType");
             mDatasetId = datasetId;
@@ -756,6 +766,7 @@
                     NO_SAVE_UI_REASON_NONE, NO_SAVE_UI_REASON_DATASET_MATCH,
                     "saveDialogNotShowReason");
             mUiType = uiType;
+            mFocusedId = focusedId;
         }
 
         @Override
@@ -852,13 +863,17 @@
                                 : null;
                         final int saveDialogNotShowReason = parcel.readInt();
                         final int uiType = parcel.readInt();
+                        AutofillId focusedId = null;
+                        if (Flags.addLastFocusedIdToFillEventHistory()) {
+                            focusedId = parcel.readParcelable(null, AutofillId.class);
+                        }
 
                         selection.addEvent(new Event(eventType, datasetId, clientState,
                                 selectedDatasetIds, ignoredDatasets,
                                 changedFieldIds, changedDatasetIds,
                                 manuallyFilledFieldIds, manuallyFilledDatasetIds,
                                 detectedFieldIds, detectedFieldClassifications,
-                                saveDialogNotShowReason, uiType));
+                                saveDialogNotShowReason, uiType, focusedId));
                     }
                     return selection;
                 }
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 8019e67..7faa5d7 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -242,13 +242,6 @@
 }
 
 flag {
-    name: "enable_desktop_windowing_app_handle_education_integration"
-    namespace: "lse_desktop_experience"
-    description: "Enables desktop windowing app handle education and integrates new APIs"
-    bug: "380272815"
-}
-
-flag {
     name: "enable_desktop_windowing_transitions"
     namespace: "lse_desktop_experience"
     description: "Enables desktop windowing transition & motion polish changes"
@@ -304,6 +297,13 @@
 }
 
 flag {
+    name: "enable_desktop_windowing_app_to_web_education_integration"
+    namespace: "lse_desktop_experience"
+    description: "Enables desktop windowing App-to-Web education and integrates new APIs"
+    bug: "380272815"
+}
+
+flag {
     name: "enable_minimize_button"
     namespace: "lse_desktop_experience"
     description: "Adds a minimize button the the caption bar"
diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml
index 78299ab..7bb6fcf 100644
--- a/core/res/res/layout/side_fps_toast.xml
+++ b/core/res/res/layout/side_fps_toast.xml
@@ -25,7 +25,7 @@
         android:layout_height="wrap_content"
         android:layout_width="0dp"
         android:layout_weight="6"
-        android:paddingBottom="10dp"
+        android:paddingBottom="16dp"
         android:text="@string/fp_power_button_enrollment_title"
         android:textColor="@color/side_fps_text_color"
         android:paddingLeft="20dp"/>
@@ -37,7 +37,7 @@
         android:layout_height="wrap_content"
         android:layout_width="0dp"
         android:layout_weight="3"
-        android:paddingBottom="10dp"
+        android:paddingBottom="16dp"
         android:text="@string/fp_power_button_enrollment_button_text"
         style="?android:attr/buttonBarNegativeButtonStyle"
         android:textColor="@color/side_fps_button_color"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 982fda0..aa954fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -194,6 +194,7 @@
     @VisibleForTesting
     static void updateRelayoutParams(
             RelayoutParams relayoutParams,
+            @NonNull Context context,
             ActivityManager.RunningTaskInfo taskInfo,
             boolean applyStartTransactionOnDraw,
             boolean shouldSetTaskVisibilityPositionAndCrop,
@@ -206,9 +207,11 @@
         relayoutParams.mRunningTaskInfo = taskInfo;
         relayoutParams.mLayoutResId = R.layout.caption_window_decor;
         relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
-        relayoutParams.mShadowRadiusId = hasGlobalFocus
-                ? R.dimen.freeform_decor_shadow_focused_thickness
-                : R.dimen.freeform_decor_shadow_unfocused_thickness;
+        relayoutParams.mShadowRadius = hasGlobalFocus
+                ? context.getResources().getDimensionPixelSize(
+                        R.dimen.freeform_decor_shadow_focused_thickness)
+                : context.getResources().getDimensionPixelSize(
+                        R.dimen.freeform_decor_shadow_unfocused_thickness);
         relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
         relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
         relayoutParams.mIsCaptionVisible = taskInfo.isFreeform()
@@ -251,7 +254,7 @@
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
-        updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw,
+        updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
                 shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible,
                 mIsKeyguardVisibleAndOccluded,
                 mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 96cc559..5eb0312 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -980,10 +980,15 @@
             relayoutParams.mInputFeatures
                     |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
         }
-        if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ hasGlobalFocus)) {
-            relayoutParams.mShadowRadiusId = hasGlobalFocus
-                    ? R.dimen.freeform_decor_shadow_focused_thickness
-                    : R.dimen.freeform_decor_shadow_unfocused_thickness;
+        if (isAppHeader
+                && DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ hasGlobalFocus)) {
+            relayoutParams.mShadowRadius = hasGlobalFocus
+                    ? context.getResources().getDimensionPixelSize(
+                            R.dimen.freeform_decor_shadow_focused_thickness)
+                    : context.getResources().getDimensionPixelSize(
+                            R.dimen.freeform_decor_shadow_unfocused_thickness);
+        } else {
+            relayoutParams.mShadowRadius = INVALID_SHADOW_RADIUS;
         }
         relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
         relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 852eee5f..584ee39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -17,7 +17,6 @@
 package com.android.wm.shell.windowdecor;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
 import static android.view.WindowInsets.Type.captionBar;
 import static android.view.WindowInsets.Type.mandatorySystemGestures;
@@ -110,6 +109,10 @@
      * Invalid corner radius that signifies that corner radius should not be set.
      */
     static final int INVALID_CORNER_RADIUS = -1;
+    /**
+     * Invalid corner radius that signifies that shadow radius should not be set.
+     */
+    static final int INVALID_SHADOW_RADIUS = -1;
 
     /**
      * System-wide context. Only used to create context with overridden configurations.
@@ -439,16 +442,10 @@
                     .setPosition(mTaskSurface, taskPosition.x, taskPosition.y);
         }
 
-        float shadowRadius;
-        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-            // Shadow is not needed for fullscreen tasks
-            shadowRadius = 0;
-        } else {
-            shadowRadius =
-                    loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId);
+        if (params.mShadowRadius != INVALID_SHADOW_RADIUS) {
+            startT.setShadowRadius(mTaskSurface, params.mShadowRadius);
+            finishT.setShadowRadius(mTaskSurface, params.mShadowRadius);
         }
-        startT.setShadowRadius(mTaskSurface, shadowRadius);
-        finishT.setShadowRadius(mTaskSurface, shadowRadius);
 
         if (params.mSetTaskVisibilityPositionAndCrop) {
             startT.show(mTaskSurface);
@@ -851,8 +848,8 @@
         @InsetsSource.Flags int mInsetSourceFlags;
         final Region mDisplayExclusionRegion = Region.obtain();
 
-        int mShadowRadiusId;
-        int mCornerRadius;
+        int mShadowRadius = INVALID_SHADOW_RADIUS;
+        int mCornerRadius = INVALID_CORNER_RADIUS;
 
         int mCaptionTopPadding;
         boolean mIsCaptionVisible;
@@ -874,8 +871,8 @@
             mInsetSourceFlags = 0;
             mDisplayExclusionRegion.setEmpty();
 
-            mShadowRadiusId = Resources.ID_NULL;
-            mCornerRadius = 0;
+            mShadowRadius = INVALID_SHADOW_RADIUS;
+            mCornerRadius = INVALID_SHADOW_RADIUS;
 
             mCaptionTopPadding = 0;
             mIsCaptionVisible = false;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
index 59141ca..b856a28 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
@@ -48,6 +48,7 @@
 
         CaptionWindowDecoration.updateRelayoutParams(
             relayoutParams,
+            mContext,
             taskInfo,
             true,
             false,
@@ -71,6 +72,7 @@
 
         CaptionWindowDecoration.updateRelayoutParams(
             relayoutParams,
+            mContext,
             taskInfo,
             true,
             false,
@@ -90,6 +92,7 @@
         val relayoutParams = WindowDecoration.RelayoutParams()
         CaptionWindowDecoration.updateRelayoutParams(
             relayoutParams,
+            mContext,
             taskInfo,
             true,
             false,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index e390fbb..03c7c98 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -61,7 +61,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -295,8 +294,9 @@
     }
 
     @Test
-    public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreEnabled() {
+    public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreSetForFreeform() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
         RelayoutParams relayoutParams = new RelayoutParams();
 
         DesktopModeWindowDecoration.updateRelayoutParams(
@@ -309,7 +309,46 @@
                 /* hasGlobalFocus= */ true,
                 mExclusionRegion);
 
-        assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
+        assertThat(relayoutParams.mShadowRadius)
+                .isNotEqualTo(WindowDecoration.INVALID_SHADOW_RADIUS);
+    }
+
+    @Test
+    public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForFullscreen() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false,
+                /* isStatusBarVisible */ true,
+                /* isKeyguardVisibleAndOccluded */ false,
+                /* inFullImmersiveMode */ false,
+                new InsetsState(),
+                /* hasGlobalFocus= */ true,
+                mExclusionRegion);
+
+        assertThat(relayoutParams.mShadowRadius).isEqualTo(WindowDecoration.INVALID_SHADOW_RADIUS);
+    }
+
+    @Test
+    public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForSplit() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false,
+                /* isStatusBarVisible */ true,
+                /* isKeyguardVisibleAndOccluded */ false,
+                /* inFullImmersiveMode */ false,
+                new InsetsState(),
+                /* hasGlobalFocus= */ true,
+                mExclusionRegion);
+
+        assertThat(relayoutParams.mShadowRadius).isEqualTo(WindowDecoration.INVALID_SHADOW_RADIUS);
     }
 
     @Test
@@ -359,6 +398,29 @@
     }
 
     @Test
+    public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForSplit() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        fillRoundedCornersResources(/* fillValue= */ 30);
+        RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false,
+                /* isStatusBarVisible */ true,
+                /* isKeyguardVisibleAndOccluded */ false,
+                /* inFullImmersiveMode */ false,
+                new InsetsState(),
+                /* hasGlobalFocus= */ true,
+                mExclusionRegion);
+
+        assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
     public void updateRelayoutParams_appHeader_usesTaskDensity() {
         final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 534803d..04b2be0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -114,6 +114,7 @@
     private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
     private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
     private static final int CORNER_RADIUS = 20;
+    private static final int SHADOW_RADIUS = 10;
     private static final int STATUS_BAR_INSET_SOURCE_ID = 0;
 
     @Rule
@@ -162,7 +163,7 @@
         mRelayoutParams.mLayoutResId = 0;
         mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
         mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width;
-        mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
+        mRelayoutParams.mShadowRadius = SHADOW_RADIUS;
         mRelayoutParams.mCornerRadius = CORNER_RADIUS;
 
         when(mMockDisplayController.getDisplay(Display.DEFAULT_DISPLAY))
@@ -280,7 +281,7 @@
 
         verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
         verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
-        verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, 10);
+        verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, SHADOW_RADIUS);
 
         assertEquals(300, mRelayoutResult.mWidth);
         assertEquals(100, mRelayoutResult.mHeight);
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 3fac74b..7b0bd04 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -74,6 +74,130 @@
          */
         public static final String PARAMETER_SATURATION = "saturation";
 
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR = "color";
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_HUE = "hue";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_BACKLIGHT = "backlight";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_BRIGHTNESS = "color_tuner_brightness";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_SATURATION = "color_tuner_saturation";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_HUE = "color_tuner_hue";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_REDO_FFSET = "color_tuner_red_offset";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_GREEN_OFFSET = "color_tuner_green_offset";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_BLUE_OFFSET = "color_tuner_blue_offset";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_RED_GAIN = "color_tuner_red_gain";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_GREEN_GAIN = "color_tuner_green_gain";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNER_BLUE_GAIN = "color_tuner_blue_gain";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_AI_PQ = "ai_pq";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_AI_SUPER_RESOLUTION = "ai_super_resolution";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_NOISE_REDUCTION = "noise_reduction";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_MPEG_NOISE_REDUCTION = "mpeg_noise_reduction";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_FLESH_TONE = "flesh_tone";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DECONTOUR = "decontour";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DYNAMIC_LUMA_CONTROL = "dynamic_luma_control";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_FILM_MODE = "film_mode";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_BLACK_STRETCH = "black_stretch";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_BLUE_STRETCH = "blue_stretch";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TUNE = "color_tune";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_COLOR_TEMPERATURE = "color_temperature";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_GLOBAL_DIMMING = "global_dimming";
+
         private PictureQuality() {
         }
     }
@@ -105,6 +229,129 @@
          */
         public static final String PARAMETER_TREBLE = "treble";
 
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_SOUND_MODE = "sound_mode";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_SURROUND_SOUND = "surround_sound";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_EQUALIZER_DETAIL = "equalizer_detail";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_SPEAKERS = "speakers";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_SPEAKERS_DELAY = "speakers_delay";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_EARC = "earc";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_AUTO_VOLUME_CONTROL = "auto_volume_control";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DOWN_MIX_MODE = "down_mix_mode";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_DRC = "dts_drc";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DOLBY_AUDIO_PROCESSING = "dolby_audio_processing";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE =
+                "dolby_audio_processing_sound_mode";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER =
+                "dolby_audio_processing_volume_leveler";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER =
+                "dolby_audio_processing_surround_virtualizer";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS =
+                "dolby_audio_processing_dolby_atmos";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DIALOGUE_ENHANCER = "dialogue_enhancer";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_VIRTUAL_X = "dts_virtual_x";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_VIRTUAL_X_TBHDX = "dts_virtual_x_tbhdx";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_VIRTUAL_X_LIMITER = "dts_virtual_x_limiter";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X =
+                "dts_virtual_x_tru_surround_x";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD =
+                "dts_virtual_x_tru_volume_hd";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY =
+                "dts_virtual_x_dialog_clarity";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_VIRTUAL_X_DEFINITION = "dts_virtual_x_definition";
+
+        /**
+         * @hide
+         */
+        public static final String PARAMETER_DTS_VIRTUAL_X_HEIGHT = "dts_virtual_x_height";
+
+
         private SoundQuality() {
         }
     }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 8e7180c..935ea25 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -119,7 +119,8 @@
                 Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
                 Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
                 Settings.System.NOTIFICATION_COOLDOWN_ALL,
-                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+                Settings.System.PREFERRED_REGION
         ));
         if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
             settings.add(Settings.System.PEAK_REFRESH_RATE);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index cfc7743..9938139 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -265,5 +265,6 @@
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.PREFERRED_REGION, ANY_STRING_VALIDATOR);
     }
 }
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
index 10d7352..e3f5378 100644
--- a/packages/SystemUI/aconfig/biometrics_framework.aconfig
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -12,3 +12,10 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+    name: "cont_auth_plugin"
+    namespace: "biometrics_framework"
+    description: "Plugin and related API hooks for contextual auth plugins"
+    bug: "373600589"
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 20b83e1..0d654d9 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1201,6 +1201,16 @@
 }
 
 flag {
+  name: "communal_hub_use_thread_pool_for_widgets"
+  namespace: "systemui"
+  description: "Use a dedicated thread pool executor for loading widgets on glanceable hub"
+  bug: "369412569"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "communal_standalone_support"
   namespace: "systemui"
   description: "Support communal features without a dock"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 91f9cce..b8d4bb4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -667,6 +667,7 @@
             faceProps,
             wakefulnessLifecycle,
             userManager,
+            null /* authContextPlugins */,
             lockPatternUtils,
             interactionJankMonitor,
             { promptSelectorInteractor },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 2dcbdc8..2817f55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -117,6 +117,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.Random;
 
 @RunWith(AndroidJUnit4.class)
@@ -1187,7 +1188,8 @@
         TestableAuthController(Context context) {
             super(context, null /* applicationCoroutineScope */,
                     mExecution, mCommandQueue, mActivityTaskManager, mWindowManager,
-                    mFingerprintManager, mFaceManager, () -> mUdfpsController, mDisplayManager,
+                    mFingerprintManager, mFaceManager, Optional.empty(),
+                    () -> mUdfpsController, mDisplayManager,
                     mWakefulnessLifecycle, mUserManager, mLockPatternUtils, () -> mUdfpsLogger,
                     () -> mLogContextInteractor, () -> mPromptSelectionInteractor,
                     () -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/AuthContextPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/AuthContextPlugin.kt
new file mode 100644
index 0000000..773c2a2
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/AuthContextPlugin.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins
+
+import android.os.IBinder
+import android.view.View
+import com.android.systemui.plugins.annotations.ProvidesInterface
+
+/**
+ * Plugin for experimental "Contextual Auth" features.
+ *
+ * These plugins will get raw access to low-level events about the user's environment, such as
+ * moving in/out of trusted locations, connection status of trusted devices, auth attempts, etc.
+ * They will also receive callbacks related to system events & transitions to enable prototypes on
+ * sensitive surfaces like lock screen and BiometricPrompt.
+ *
+ * Note to rebuild the plugin jar run: m PluginDummyLib
+ */
+@ProvidesInterface(action = AuthContextPlugin.ACTION, version = AuthContextPlugin.VERSION)
+interface AuthContextPlugin : Plugin {
+
+    /**
+     * Called in the background when the plugin is enabled.
+     *
+     * This is a good time to ask your friendly [saucier] to cook up something special. The
+     * [Plugin.onCreate] can also be used for initialization.
+     */
+    fun activated(saucier: Saucier)
+
+    /**
+     * Called when a [SensitiveSurface] is first shown.
+     *
+     * This may be called repeatedly if the state of the surface changes after it is shown. For
+     * example, [SensitiveSurface.BiometricPrompt.isCredential] will change if the user falls back
+     * to a credential-based auth method.
+     */
+    fun onShowingSensitiveSurface(surface: SensitiveSurface)
+
+    /**
+     * Called when a [SensitiveSurface] sensitive surface is hidden.
+     *
+     * This method may still be called without [onShowingSensitiveSurface] in cases of rapid
+     * dismissal and plugins implementations should typically be idempotent.
+     */
+    fun onHidingSensitiveSurface(surface: SensitiveSurface)
+
+    companion object {
+        /** Plugin action. */
+        const val ACTION = "com.android.systemui.action.PLUGIN_AUTH_CONTEXT"
+        /** Plugin version. */
+        const val VERSION = 1
+    }
+
+    /** Information about a sensitive surface in the framework, which the Plugin may augment. */
+    sealed interface SensitiveSurface {
+
+        /** Information about the BiometricPrompt that is being shown to the user. */
+        data class BiometricPrompt(val view: View? = null, val isCredential: Boolean = false) :
+            SensitiveSurface
+
+        /** Information about bouncer. */
+        data class LockscreenBouncer(val view: View? = null) : SensitiveSurface
+    }
+
+    /** Ask for the special. */
+    interface Saucier {
+
+        /** What [flavor] would you like? */
+        fun getSauce(flavor: String): IBinder?
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index b491c94..f6b6655 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -64,6 +64,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
+import com.android.systemui.biometrics.plugins.AuthContextPlugins;
 import com.android.systemui.biometrics.shared.model.BiometricModalities;
 import com.android.systemui.biometrics.shared.model.PromptKind;
 import com.android.systemui.biometrics.ui.CredentialView;
@@ -132,6 +133,7 @@
     private final int mEffectiveUserId;
     private final IBinder mWindowToken = new Binder();
     private final ViewCaptureAwareWindowManager mWindowManager;
+    @Nullable private final AuthContextPlugins mAuthContextPlugins;
     private final Interpolator mLinearOutSlowIn;
     private final LockPatternUtils mLockPatternUtils;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
@@ -289,6 +291,7 @@
             @Nullable List<FaceSensorPropertiesInternal> faceProps,
             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
             @NonNull UserManager userManager,
+            @Nullable AuthContextPlugins authContextPlugins,
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull InteractionJankMonitor jankMonitor,
             @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider,
@@ -306,6 +309,7 @@
         WindowManager wm = getContext().getSystemService(WindowManager.class);
         mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture,
                 enableViewCaptureTracing());
+        mAuthContextPlugins = authContextPlugins;
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mApplicationCoroutineScope = applicationCoroutineScope;
 
@@ -446,7 +450,7 @@
         final CredentialViewModel vm = mCredentialViewModelProvider.get();
         vm.setAnimateContents(animateContents);
         ((CredentialView) mCredentialView).init(vm, this, mPanelController, animatePanel,
-                mBiometricCallback);
+                mBiometricCallback, mAuthContextPlugins);
 
         mLayout.addView(mCredentialView);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a5bd559..4faf6ff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -21,6 +21,7 @@
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.systemui.Flags.contAuthPlugin;
 import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
 
 import android.annotation.NonNull;
@@ -74,6 +75,7 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
+import com.android.systemui.biometrics.plugins.AuthContextPlugins;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
@@ -108,6 +110,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -139,6 +142,7 @@
     private final ActivityTaskManager mActivityTaskManager;
     @Nullable private final FingerprintManager mFingerprintManager;
     @Nullable private final FaceManager mFaceManager;
+    @Nullable private final AuthContextPlugins mContextPlugins;
     private final Provider<UdfpsController> mUdfpsControllerFactory;
     private final CoroutineScope mApplicationCoroutineScope;
     private Job mBiometricContextListenerJob = null;
@@ -717,6 +721,7 @@
             @NonNull WindowManager windowManager,
             @Nullable FingerprintManager fingerprintManager,
             @Nullable FaceManager faceManager,
+            Optional<AuthContextPlugins> contextPlugins,
             Provider<UdfpsController> udfpsControllerFactory,
             @NonNull DisplayManager displayManager,
             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
@@ -744,6 +749,7 @@
         mActivityTaskManager = activityTaskManager;
         mFingerprintManager = fingerprintManager;
         mFaceManager = faceManager;
+        mContextPlugins = contAuthPlugin() ? contextPlugins.orElse(null) : null;
         mUdfpsControllerFactory = udfpsControllerFactory;
         mUdfpsLogger = udfpsLogger;
         mDisplayManager = displayManager;
@@ -858,6 +864,10 @@
         mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
         mOrientationListener.enable();
         updateSensorLocations();
+
+        if (mContextPlugins != null) {
+            mContextPlugins.activate();
+        }
     }
 
     @Override
@@ -1350,7 +1360,7 @@
         config.mSensorIds = sensorIds;
         config.mScaleProvider = this::getScaleFactor;
         return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
-                wakefulnessLifecycle, userManager, lockPatternUtils,
+                wakefulnessLifecycle, userManager, mContextPlugins, lockPatternUtils,
                 mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
                 mCredentialViewModelProvider, bgExecutor, mVibratorHelper,
                 mLazyViewCapture, mMSDLPlayer);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index e2a8a69..60ce177 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl
 import com.android.systemui.biometrics.data.repository.PromptRepository
 import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
+import com.android.systemui.biometrics.plugins.AuthContextPlugins
 import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
 import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
 import com.android.systemui.biometrics.udfps.OverlapDetector
@@ -58,7 +59,7 @@
 /** Dagger module for all things biometric. */
 @Module
 interface BiometricsModule {
-    /** Starts AuthController.  */
+    /** Starts AuthController. */
     @Binds
     @IntoMap
     @ClassKey(AuthController::class)
@@ -103,8 +104,9 @@
     @SysUISingleton
     fun displayStateRepository(impl: DisplayStateRepositoryImpl): DisplayStateRepository
 
-    @BindsOptionalOf
-    fun deviceEntryUnlockTrackerViewBinder(): DeviceEntryUnlockTrackerViewBinder
+    @BindsOptionalOf fun authContextPlugins(): AuthContextPlugins
+
+    @BindsOptionalOf fun deviceEntryUnlockTrackerViewBinder(): DeviceEntryUnlockTrackerViewBinder
 
     companion object {
         /** Background [Executor] for HAL related operations. */
@@ -117,8 +119,7 @@
 
         @Provides fun providesUdfpsUtils(): UdfpsUtils = UdfpsUtils()
 
-        @Provides
-        fun provideIconProvider(context: Context): IconProvider = IconProvider(context)
+        @Provides fun provideIconProvider(context: Context): IconProvider = IconProvider(context)
 
         @Provides
         @SysUISingleton
@@ -136,7 +137,7 @@
                     EllipseOverlapDetectorParams(
                         minOverlap = values[3],
                         targetSize = values[2],
-                        stepSize = values[4].toInt()
+                        stepSize = values[4].toInt(),
                     )
                 )
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/plugins/AuthContextPlugins.kt b/packages/SystemUI/src/com/android/systemui/biometrics/plugins/AuthContextPlugins.kt
new file mode 100644
index 0000000..ca38e98
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/plugins/AuthContextPlugins.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.biometrics.plugins
+
+import com.android.systemui.plugins.AuthContextPlugin
+import com.android.systemui.plugins.PluginManager
+
+/** Wrapper interface for registering & forwarding events to all available [AuthContextPlugin]s. */
+interface AuthContextPlugins {
+    /** Finds and actives all plugins via SysUI's [PluginManager] (should be called at startup). */
+    fun activate()
+
+    /**
+     * Interact with all registered plugins.
+     *
+     * The provided [block] will be repeated for each available plugin.
+     */
+    suspend fun use(block: (AuthContextPlugin) -> Unit)
+
+    /**
+     * Like [use] but when no existing coroutine context is available.
+     *
+     * The [block] will be run on SysUI's general background context and can, optionally, be
+     * confined to [runOnMain] (defaults to a background thread).
+     */
+    fun useInBackground(runOnMain: Boolean = false, block: (AuthContextPlugin) -> Unit)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index b28733f..dad140f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -11,6 +11,7 @@
 import android.widget.LinearLayout
 import android.widget.TextView
 import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.biometrics.plugins.AuthContextPlugins
 import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
 import com.android.systemui.biometrics.ui.binder.Spaghetti
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
@@ -33,6 +34,7 @@
         panelViewController: AuthPanelController,
         animatePanel: Boolean,
         legacyCallback: Spaghetti.Callback,
+        plugins: AuthContextPlugins?,
     ) {
         CredentialViewBinder.bind(
             this,
@@ -40,7 +42,8 @@
             viewModel,
             panelViewController,
             animatePanel,
-            legacyCallback
+            legacyCallback,
+            plugins,
         )
     }
 
@@ -78,7 +81,7 @@
             0,
             statusBarInsets.top,
             0,
-            if (keyboardInsets.bottom == 0) navigationInsets.bottom else keyboardInsets.bottom
+            if (keyboardInsets.bottom == 0) navigationInsets.bottom else keyboardInsets.bottom,
         )
         return WindowInsets.CONSUMED
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt
index d9d286f..e80a79b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPatternView.kt
@@ -8,6 +8,7 @@
 import android.view.WindowInsets.Type
 import android.widget.LinearLayout
 import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.biometrics.plugins.AuthContextPlugins
 import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
 import com.android.systemui.biometrics.ui.binder.Spaghetti
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
@@ -23,6 +24,7 @@
         panelViewController: AuthPanelController,
         animatePanel: Boolean,
         legacyCallback: Spaghetti.Callback,
+        plugins: AuthContextPlugins?,
     ) {
         CredentialViewBinder.bind(
             this,
@@ -30,7 +32,8 @@
             viewModel,
             panelViewController,
             animatePanel,
-            legacyCallback
+            legacyCallback,
+            plugins,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt
index e2f9895..f3e4917 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialView.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.biometrics.ui
 
 import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.biometrics.plugins.AuthContextPlugins
 import com.android.systemui.biometrics.ui.binder.Spaghetti
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
 
@@ -29,5 +30,6 @@
         panelViewController: AuthPanelController,
         animatePanel: Boolean,
         legacyCallback: Spaghetti.Callback,
+        plugins: AuthContextPlugins?,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 39543e7..10b1211 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -9,18 +9,21 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.biometrics.plugins.AuthContextPlugins
 import com.android.systemui.biometrics.ui.CredentialPasswordView
 import com.android.systemui.biometrics.ui.CredentialPatternView
 import com.android.systemui.biometrics.ui.CredentialView
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.AuthContextPlugin
 import com.android.systemui.res.R
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.onEach
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 private const val ANIMATE_CREDENTIAL_INITIAL_DURATION_MS = 150
 
@@ -42,6 +45,7 @@
         panelViewController: AuthPanelController,
         animatePanel: Boolean,
         legacyCallback: Spaghetti.Callback,
+        plugins: AuthContextPlugins?,
         maxErrorDuration: Long = 3_000L,
         requestFocusForInput: Boolean = true,
     ) {
@@ -72,6 +76,10 @@
             }
 
             repeatOnLifecycle(Lifecycle.State.STARTED) {
+                if (plugins != null) {
+                    launch { plugins.notifyShowingBPCredential(view) }
+                }
+
                 // show prompt metadata
                 launch {
                     viewModel.header.collect { header ->
@@ -136,6 +144,12 @@
                             host.onCredentialAttemptsRemaining(info.remaining!!, info.message)
                         }
                 }
+
+                try {
+                    awaitCancellation()
+                } catch (_: Throwable) {
+                    plugins?.notifyHidingBPCredential()
+                }
             }
         }
 
@@ -172,3 +186,15 @@
         text = if (gone) "" else value
     }
     get() = text?.toString()
+
+private suspend fun AuthContextPlugins.notifyShowingBPCredential(view: View) = use { plugin ->
+    plugin.onShowingSensitiveSurface(
+        AuthContextPlugin.SensitiveSurface.BiometricPrompt(view = view, isCredential = true)
+    )
+}
+
+private fun AuthContextPlugins.notifyHidingBPCredential() = useInBackground { plugin ->
+    plugin.onHidingSensitiveSurface(
+        AuthContextPlugin.SensitiveSurface.BiometricPrompt(isCredential = true)
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index 12f06bb..8a4cc63 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -3,6 +3,8 @@
 import android.view.ViewGroup
 import com.android.keyguard.KeyguardMessageAreaController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.Flags.contAuthPlugin
+import com.android.systemui.biometrics.plugins.AuthContextPlugins
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
@@ -17,6 +19,7 @@
 import com.android.systemui.log.BouncerLogger
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import dagger.Lazy
+import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,6 +63,7 @@
 constructor(
     private val legacyBouncerDependencies: Lazy<LegacyBouncerDependencies>,
     private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>,
+    private val contextPlugins: Optional<AuthContextPlugins>,
 ) {
     fun bind(view: ViewGroup) {
         if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) {
@@ -85,6 +89,7 @@
                 deps.bouncerMessageInteractor,
                 deps.bouncerLogger,
                 deps.selectedUserInteractor,
+                if (contAuthPlugin()) contextPlugins.orElse(null) else null,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 71eda0c..434a9ce 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -22,11 +22,13 @@
 import android.window.OnBackAnimationCallback
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.KeyguardMessageAreaController
 import com.android.keyguard.KeyguardSecurityContainerController
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardSecurityView
 import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.biometrics.plugins.AuthContextPlugins
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
 import com.android.systemui.bouncer.ui.BouncerViewDelegate
@@ -35,10 +37,10 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.log.BouncerLogger
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.AuthContextPlugin
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.filter
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Binds the bouncer container to its view model. */
 object KeyguardBouncerViewBinder {
@@ -52,6 +54,7 @@
         bouncerMessageInteractor: BouncerMessageInteractor,
         bouncerLogger: BouncerLogger,
         selectedUserInteractor: SelectedUserInteractor,
+        plugins: AuthContextPlugins?,
     ) {
         // Builds the KeyguardSecurityContainerController from bouncer view group.
         val securityContainerController: KeyguardSecurityContainerController =
@@ -94,7 +97,7 @@
 
                 override fun setDismissAction(
                     onDismissAction: ActivityStarter.OnDismissAction?,
-                    cancelAction: Runnable?
+                    cancelAction: Runnable?,
                 ) {
                     securityContainerController.setOnDismissAction(onDismissAction, cancelAction)
                 }
@@ -138,7 +141,7 @@
                                     it.bindMessageView(
                                         bouncerMessageInteractor,
                                         messageAreaControllerFactory,
-                                        bouncerLogger
+                                        bouncerLogger,
                                     )
                                 }
                             } else {
@@ -149,6 +152,13 @@
                                 securityContainerController.reset()
                                 securityContainerController.onPause()
                             }
+                            plugins?.apply {
+                                if (isShowing) {
+                                    notifyBouncerShowing(view)
+                                } else {
+                                    notifyBouncerGone()
+                                }
+                            }
                         }
                     }
 
@@ -209,7 +219,7 @@
                             securityContainerController.showMessage(
                                 it.message,
                                 it.colorStateList,
-                                /* animated= */ true
+                                /* animated= */ true,
                             )
                             viewModel.onMessageShown()
                         }
@@ -233,8 +243,19 @@
                     awaitCancellation()
                 } finally {
                     viewModel.setBouncerViewDelegate(null)
+                    plugins?.notifyBouncerGone()
                 }
             }
         }
     }
 }
+
+private suspend fun AuthContextPlugins.notifyBouncerShowing(view: View) = use { plugin ->
+    plugin.onShowingSensitiveSurface(
+        AuthContextPlugin.SensitiveSurface.LockscreenBouncer(view = view)
+    )
+}
+
+private fun AuthContextPlugins.notifyBouncerGone() = useInBackground { plugin ->
+    plugin.onHidingSensitiveSurface(AuthContextPlugin.SensitiveSurface.LockscreenBouncer())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
index cc6007b..50d86a2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
@@ -20,6 +20,7 @@
 import android.os.Bundle
 import android.util.SizeF
 import com.android.app.tracing.coroutines.withContextTraced as withContext
+import com.android.systemui.Flags
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
 import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate
@@ -30,6 +31,9 @@
 import com.android.systemui.dagger.qualifiers.UiBackground
 import dagger.Lazy
 import java.util.concurrent.Executor
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 
@@ -53,7 +57,11 @@
         withContext("$TAG#createWidget", uiBgContext) {
             val view =
                 CommunalAppWidgetHostView(context, interactionHandler).apply {
-                    setExecutor(uiBgExecutor)
+                    if (Flags.communalHubUseThreadPoolForWidgets()) {
+                        setExecutor(widgetExecutor)
+                    } else {
+                        setExecutor(uiBgExecutor)
+                    }
                     setAppWidget(model.appWidgetId, model.providerInfo)
                 }
 
@@ -90,5 +98,20 @@
 
     private companion object {
         const val TAG = "WidgetViewFactory"
+
+        val poolSize = Runtime.getRuntime().availableProcessors().coerceAtLeast(2)
+
+        /**
+         * This executor is used for widget inflation. Parameters match what launcher uses. See
+         * [com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR].
+         */
+        val widgetExecutor =
+            ThreadPoolExecutor(
+                /*corePoolSize*/ poolSize,
+                /*maxPoolSize*/ poolSize,
+                /*keepAlive*/ 1,
+                /*unit*/ TimeUnit.SECONDS,
+                /*workQueue*/ LinkedBlockingQueue(),
+            )
     }
 }
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index b3fe5f2..444844121 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -37,3 +37,10 @@
     description: "Improvements for Fill Dialog, including deprecation of pre-trigger API's"
     bug: "336223371"
 }
+
+flag {
+  name: "add_last_focused_id_to_fill_event_history"
+  namespace: "autofill"
+  description: "Adds focused id to each event that's part of the fill event history"
+  bug: "334141398"
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index b52c6505..cd4ace2 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -900,13 +900,13 @@
      * Updates the last fill selection when an authentication was selected.
      */
     void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
-            int uiType) {
+            int uiType, @Nullable AutofillId focusedId) {
         synchronized (mLock) {
             if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
                                 null, null, null, null, null, null,
-                                NO_SAVE_UI_REASON_NONE, uiType));
+                                NO_SAVE_UI_REASON_NONE, uiType, focusedId));
             }
         }
     }
@@ -915,13 +915,13 @@
      * Updates the last fill selection when an dataset authentication was selected.
      */
     void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
-            @Nullable Bundle clientState, int uiType) {
+            @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) {
         synchronized (mLock) {
             if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
                                 clientState, null, null, null, null, null, null, null, null,
-                                NO_SAVE_UI_REASON_NONE, uiType));
+                                NO_SAVE_UI_REASON_NONE, uiType, focusedId));
             }
         }
     }
@@ -933,7 +933,7 @@
         synchronized (mLock) {
             if (isValidEventLocked("logSaveShown()", sessionId)) {
                 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
-                        null, null, null, null, null, null, null));
+                        null, null, null, null, null, null, null, /* focusedId= */ null));
             }
         }
     }
@@ -942,13 +942,13 @@
      * Updates the last fill response when a dataset was selected.
      */
     void logDatasetSelected(@Nullable String selectedDataset, int sessionId,
-            @Nullable Bundle clientState,  int uiType) {
+            @Nullable Bundle clientState,  int uiType, @Nullable AutofillId focusedId) {
         synchronized (mLock) {
             if (isValidEventLocked("logDatasetSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
                                 null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
-                                uiType));
+                                uiType, focusedId));
             }
         }
     }
@@ -956,13 +956,14 @@
     /**
      * Updates the last fill response when a dataset is shown.
      */
-    void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType) {
+    void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType,
+            @Nullable AutofillId focusedId) {
         synchronized (mLock) {
             if (isValidEventLocked("logDatasetShown", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
                                 null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
-                                uiType));
+                                uiType, focusedId));
             }
         }
     }
@@ -970,7 +971,8 @@
     /**
      * Updates the last fill response when a view was entered.
      */
-    void logViewEntered(int sessionId, @Nullable Bundle clientState) {
+    void logViewEntered(int sessionId, @Nullable Bundle clientState,
+            @Nullable AutofillId focusedId) {
         synchronized (mLock) {
             if (!isValidEventLocked("logViewEntered", sessionId)) {
                 return;
@@ -988,7 +990,7 @@
 
             mEventHistory.addEvent(
                     new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
-                            null, null, null, null, null, null, null));
+                            null, null, null, null, null, null, null, focusedId));
         }
     }
 
@@ -1001,7 +1003,8 @@
             }
             mAugmentedAutofillEventHistory.addEvent(
                     new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
-                            clientState, null, null, null, null, null, null, null, null));
+                            clientState, null, null, null, null, null, null, null, null,
+                            /* focusedId= */ null));
         }
     }
 
@@ -1014,7 +1017,7 @@
             }
             mAugmentedAutofillEventHistory.addEvent(
                     new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
-                            null, null, null, null, null, null));
+                            null, null, null, null, null, null, /* focusedId= */ null));
         }
     }
 
@@ -1029,7 +1032,7 @@
             mAugmentedAutofillEventHistory.addEvent(
                     new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
                             null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
-                            UI_TYPE_INLINE));
+                            UI_TYPE_INLINE, /* focusedId= */ null));
 
         }
     }
@@ -1113,7 +1116,8 @@
                     clientState, selectedDatasets, ignoredDatasets,
                     changedFieldIds, changedDatasetIds,
                     manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                    detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason));
+                    detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason,
+                    /* focusedId= */ null));
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8f12b1d..6b227d7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1871,7 +1871,7 @@
 
             if (mLogViewEntered) {
                 mLogViewEntered = false;
-                mService.logViewEntered(id, null);
+                mService.logViewEntered(id, null, mCurrentViewId);
             }
         }
 
@@ -2775,9 +2775,9 @@
                 forceRemoveFromServiceLocked();
                 return;
             }
+            mService.setAuthenticationSelected(id, mClientState, uiType, mCurrentViewId);
         }
 
-        mService.setAuthenticationSelected(id, mClientState, uiType);
 
         final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
         mHandler.sendMessage(
@@ -2850,7 +2850,7 @@
                 if (!mLoggedInlineDatasetShown) {
                     // Chip inflation already logged, do not log again.
                     // This is needed because every chip inflation will call this.
-                    mService.logDatasetShown(this.id, mClientState, uiType);
+                    mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
                     Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
                 }
                 mLoggedInlineDatasetShown = true;
@@ -2858,7 +2858,7 @@
                 mPresentationStatsEventLogger.logWhenDatasetShown(numDatasetsShown);
                 // Explicitly sets maybeSetSuggestionPresentedTimestampMs
                 mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
-                mService.logDatasetShown(this.id, mClientState, uiType);
+                mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
                 Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
             }
         }
@@ -5139,7 +5139,7 @@
                         // so this calling logViewEntered will be a nop.
                         // Calling logViewEntered() twice will only log it once
                         // TODO(271181979): this is broken for multiple partitions
-                        mService.logViewEntered(this.id, null);
+                        mService.logViewEntered(this.id, null, mCurrentViewId);
                     }
 
                     // If this is the first time view is entered for inline, the last
@@ -6657,7 +6657,8 @@
             // Autofill it directly...
             if (dataset.getAuthentication() == null) {
                 if (generateEvent) {
-                    mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType);
+                    mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType,
+                            mCurrentViewId);
                 }
                 if (mCurrentViewId != null) {
                     mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
@@ -6667,7 +6668,8 @@
             }
 
             // ...or handle authentication.
-            mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType);
+            mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType,
+                        mCurrentViewId);
             mPresentationStatsEventLogger.maybeSetAuthenticationType(
                     AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
             // does not matter the value of isPrimary because null response won't be overridden.
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b9540eb..f88aa9b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -178,6 +178,7 @@
 
     static_libs: [
         "android.frameworks.vibrator-V1-java", // AIDL
+        "android.frameworks.devicestate-V1-java", // AIDL
         "android.hardware.authsecret-V1.0-java",
         "android.hardware.authsecret-V1-java",
         "android.hardware.boot-V1.0-java", // HIDL
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 0286f7b..36dff89 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -101,6 +101,9 @@
 import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
 
+import com.android.server.pm.BackgroundInstallControlService;
+import com.android.server.pm.BackgroundInstallControlCallbackHelper;
+
 /**
  * @hide
  */
@@ -138,6 +141,10 @@
     static final int MBA_STATUS_NEW_INSTALL = 3;
     // used for indicating newly installed MBAs that are updated (but unused currently)
     static final int MBA_STATUS_UPDATED_NEW_INSTALL = 4;
+    // used for indicating preloaded MBAs that are downgraded
+    static final int MBA_STATUS_DOWNGRADED_PRELOADED = 5;
+    // used for indicating MBAs that are uninstalled
+    static final int MBA_STATUS_UNINSTALLED = 6;
 
     @VisibleForTesting
     static final String KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION =
@@ -202,7 +209,9 @@
          * @param mbaStatus Assign this value of MBA status to the returned elements.
          * @return a @{@code List<IBinaryTransparencyService.AppInfo>}
          */
-        private @NonNull List<IBinaryTransparencyService.AppInfo> collectAppInfo(
+        @VisibleForTesting
+        @NonNull
+        List<IBinaryTransparencyService.AppInfo> collectAppInfo(
                 PackageState packageState, int mbaStatus) {
             // compute content digest
             if (DEBUG) {
@@ -336,27 +345,28 @@
                         + " packages after considering APEXs.");
             }
 
-            // proceed with all preloaded apps
-            List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo =
-                    collectAllUpdatedPreloadInfo(packagesMeasured);
-            for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) {
-                packagesMeasured.putBoolean(appInfo.packageName, true);
-                writeAppInfoToLog(appInfo);
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "Measured " + packagesMeasured.size()
-                        + " packages after considering preloads");
-            }
-
-            if (!android.app.Flags.backgroundInstallControlCallbackApi()
-                    && CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
-                // lastly measure all newly installed MBAs
-                List<IBinaryTransparencyService.AppInfo> allMbaInfo =
-                        collectAllSilentInstalledMbaInfo(packagesMeasured);
-                for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) {
+            if (!android.app.Flags.backgroundInstallControlCallbackApi()) {
+                // proceed with all preloaded apps
+                List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo =
+                        collectAllUpdatedPreloadInfo(packagesMeasured);
+                for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) {
                     packagesMeasured.putBoolean(appInfo.packageName, true);
                     writeAppInfoToLog(appInfo);
                 }
+                if (DEBUG) {
+                    Slog.d(TAG, "Measured " + packagesMeasured.size()
+                            + " packages after considering preloads");
+                }
+
+                if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
+                    // lastly measure all newly installed MBAs
+                    List<IBinaryTransparencyService.AppInfo> allMbaInfo =
+                            collectAllSilentInstalledMbaInfo(packagesMeasured);
+                    for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) {
+                        packagesMeasured.putBoolean(appInfo.packageName, true);
+                        writeAppInfoToLog(appInfo);
+                    }
+                }
             }
             long timeSpentMeasuring = System.currentTimeMillis() - currentTimeMs;
             digestAllPackagesLatency.logSample(timeSpentMeasuring);
@@ -466,7 +476,8 @@
                     apexInfo.signerDigests);
         }
 
-        private void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) {
+        @VisibleForTesting
+        void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) {
             // Must order by the proto's field number.
             FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
                     appInfo.packageName,
@@ -1165,40 +1176,85 @@
      * TODO: Add a host test for testing registration and callback of BicCallbackHandler
      *  b/380002484
      */
+    @VisibleForTesting
     static class BicCallbackHandler extends IRemoteCallback.Stub {
-        private static final String BIC_CALLBACK_HANDLER_TAG =
-                "BTS.BicCallbackHandler";
-        private final BinaryTransparencyServiceImpl mServiceImpl;
-        static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
+        private static final String BIC_CALLBACK_HANDLER_TAG = TAG + ".BicCallbackHandler";
 
-        BicCallbackHandler(BinaryTransparencyServiceImpl impl) {
-            mServiceImpl = impl;
+        private static final int INSTALL_EVENT_TYPE_UNSET = -1;
+
+        private final IBicAppInfoHelper mBicAppInfoHelper;
+
+        @VisibleForTesting
+        BicCallbackHandler(IBicAppInfoHelper bicAppInfoHelper) {
+            mBicAppInfoHelper = bicAppInfoHelper;
         }
 
         @Override
         public void sendResult(Bundle data) {
-            String packageName = data.getString(FLAGGED_PACKAGE_NAME_KEY);
-            if (packageName == null) return;
-            if (DEBUG) {
-                Slog.d(BIC_CALLBACK_HANDLER_TAG, "background install event detected for "
-                        + packageName);
+            String packageName = data.getString(
+                    BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY);
+            int installType = data.getInt(
+                    BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY,
+                    INSTALL_EVENT_TYPE_UNSET);
+            if (packageName == null || installType == INSTALL_EVENT_TYPE_UNSET) {
+                Slog.w(BIC_CALLBACK_HANDLER_TAG, "Package name or install type is "
+                        + "unavailable, ignoring event");
+                return;
             }
-
-            PackageState packageState = LocalServices.getService(PackageManagerInternal.class)
+            Slog.d(BIC_CALLBACK_HANDLER_TAG, "Detected new bic event for: " + packageName);
+            if (installType == BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL) {
+                PackageState packageState = LocalServices.getService(PackageManagerInternal.class)
                     .getPackageStateInternal(packageName);
-            if (packageState == null) {
-                Slog.w(TAG, "Package state is unavailable, ignoring the package "
-                        + packageName);
-                return;
+                if (packageState == null) {
+                    Slog.w(TAG, "Package state is unavailable, ignoring the package "
+                            + packageName);
+                    return;
+                }
+                int mbaStatus = MBA_STATUS_NEW_INSTALL;
+                if (packageState.isUpdatedSystemApp()) {
+                    mbaStatus = MBA_STATUS_UPDATED_PRELOAD;
+                }
+                List<IBinaryTransparencyService.AppInfo> mbaInfo = mBicAppInfoHelper.collectAppInfo(
+                        packageState, mbaStatus);
+                for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) {
+                    mBicAppInfoHelper.writeAppInfoToLog(appInfo);
+                }
+            } else if (installType
+                        == BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL) {
+                IBinaryTransparencyService.AppInfo appInfo
+                    = new IBinaryTransparencyService.AppInfo();
+                // since app is already uninstalled we won't be able to retrieve additional
+                // info on it.
+                appInfo.packageName = packageName;
+                appInfo.mbaStatus = MBA_STATUS_UNINSTALLED;
+                mBicAppInfoHelper.writeAppInfoToLog(appInfo);
+            } else {
+                Slog.w(BIC_CALLBACK_HANDLER_TAG, "Unsupported BIC event: " + installType);
             }
-            if (packageState.isUpdatedSystemApp()) {
-                return;
-            }
-            List<IBinaryTransparencyService.AppInfo> mbaInfo = mServiceImpl.collectAppInfo(
-                    packageState, MBA_STATUS_NEW_INSTALL);
-            for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) {
-                mServiceImpl.writeAppInfoToLog(appInfo);
-            }
+        }
+
+        /**
+         * A wrapper of interface for{@link FrameworkStatsLog and ApkDigests}
+         * for easier testing
+         */
+        @VisibleForTesting
+        public interface IBicAppInfoHelper {
+
+            /**
+             * A wrapper of {@link FrameworkStatsLog}
+             *
+             * @param appInfo The app info of the changed MBA to be logged
+             */
+            public void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo);
+
+            /**
+             * A wrapper of {@link BinaryTransparencyServiceImpl}
+             *
+             * @param packageState The packageState provided retrieved from PackageManagerInternal
+             * @param mbaStatus The MBA status of the package
+             */
+            public List<IBinaryTransparencyService.AppInfo> collectAppInfo(
+                PackageState packageState, int mbaStatus);
         }
     };
 
@@ -1596,7 +1652,21 @@
         }
         try {
             iBics.registerBackgroundInstallCallback(
-                    new BicCallbackHandler(mServiceImpl));
+                    new BicCallbackHandler(
+                        new BicCallbackHandler.IBicAppInfoHelper() {
+                            @Override
+                            public void writeAppInfoToLog(
+                                    IBinaryTransparencyService.AppInfo appInfo) {
+                                mServiceImpl.writeAppInfoToLog(appInfo);
+                            }
+
+                            @Override
+                            public List<IBinaryTransparencyService.AppInfo> collectAppInfo(
+                                PackageState packageState, int mbaStatus) {
+                                return mServiceImpl.collectAppInfo(packageState, mbaStatus);
+                            }
+                        }
+                    ));
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to register BackgroundInstallControl callback.");
         }
@@ -1643,8 +1713,12 @@
             }
 
             String packageName = data.getSchemeSpecificPart();
-            // now we've got to check what package is this
-            if (isPackagePreloaded(packageName) || isPackageAnApex(packageName)) {
+
+            boolean shouldMeasureMba =
+                !android.app.Flags.backgroundInstallControlCallbackApi()
+                && isPackagePreloaded(packageName);
+
+            if (shouldMeasureMba || isPackageAnApex(packageName)) {
                 Slog.d(TAG, packageName + " was updated. Scheduling measurement...");
                 UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
                         BinaryTransparencyService.this);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 053ec82..01daceb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -132,7 +132,7 @@
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.security.Flags.preventIntentRedirect;
 import static android.security.Flags.preventIntentRedirectCollectNestedKeysOnServerIfNotCollected;
-import static android.security.Flags.preventIntentRedirectShowToast;
+import static android.security.Flags.preventIntentRedirectShowToastIfNestedKeysNotCollected;
 import static android.security.Flags.preventIntentRedirectThrowExceptionIfNestedKeysNotCollected;
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 import static android.view.Display.INVALID_DISPLAY;
@@ -19326,7 +19326,7 @@
                     "[IntentRedirect] The intent does not have its nested keys collected as a "
                             + "preparation for creating intent creator tokens. Intent: "
                             + intent + "; creatorPackage: " + creatorPackage);
-            if (preventIntentRedirectShowToast()) {
+            if (preventIntentRedirectShowToastIfNestedKeysNotCollected()) {
                 UiThread.getHandler().post(
                         () -> Toast.makeText(mContext,
                                 "Nested keys not collected. go/report-bug-intentRedir to report a"
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 8b9c664..2513443 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,16 @@
 import static android.Manifest.permission.CONTROL_DEVICE_STATE;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_DUAL_DISPLAY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_REAR_DISPLAY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
 import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST;
 import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS;
@@ -44,6 +53,10 @@
 import android.app.ActivityManagerInternal;
 import android.app.IProcessObserver;
 import android.content.Context;
+import android.frameworks.devicestate.DeviceStateConfiguration;
+import android.frameworks.devicestate.ErrorCode;
+import android.frameworks.devicestate.IDeviceStateListener;
+import android.frameworks.devicestate.IDeviceStateService;
 import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.DeviceStateManager;
@@ -56,9 +69,12 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.ShellCallback;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.util.LongSparseLongArray;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -82,6 +98,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.WeakHashMap;
@@ -130,6 +147,8 @@
     @NonNull
     private final BinderService mBinderService;
     @NonNull
+    private final HalService mHalService;
+    @NonNull
     private final OverrideRequestController mOverrideRequestController;
     @NonNull
     private final DeviceStateProviderListener mDeviceStateProviderListener;
@@ -139,7 +158,7 @@
 
     // All supported device states keyed by identifier.
     @GuardedBy("mLock")
-    private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
+    private final SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
 
     // The current committed device state. Will be empty until the first device state provided by
     // the DeviceStateProvider is committed.
@@ -177,7 +196,7 @@
     @GuardedBy("mLock")
     private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
 
-    private Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
+    private final Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
 
     private Set<Integer> mFoldedDeviceStates = new HashSet<>();
 
@@ -259,6 +278,7 @@
         mDeviceStateProviderListener = new DeviceStateProviderListener();
         mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
         mBinderService = new BinderService();
+        mHalService = new HalService();
         mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
         mDeviceStateNotificationController = new DeviceStateNotificationController(
                 context, mHandler,
@@ -272,6 +292,10 @@
     @Override
     public void onStart() {
         publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
+        String halServiceName = IDeviceStateService.DESCRIPTOR + "/default";
+        if (ServiceManager.isDeclared(halServiceName)) {
+            publishBinderService(halServiceName, mHalService);
+        }
         publishLocalService(DeviceStateManagerInternal.class, new LocalService());
 
         if (!Flags.deviceStatePropertyMigration()) {
@@ -440,6 +464,11 @@
         return mBinderService;
     }
 
+    @VisibleForTesting
+    IDeviceStateService getHalBinderService() {
+        return mHalService;
+    }
+
     private void updateSupportedStates(DeviceState[] supportedDeviceStates,
             @DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
         synchronized (mLock) {
@@ -1282,6 +1311,124 @@
         }
     }
 
+    private final class HalService extends IDeviceStateService.Stub {
+        private final LongSparseLongArray mPublicProperties = new LongSparseLongArray();
+        public HalService() {
+            mPublicProperties.put(
+                    DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
+                    FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED);
+            mPublicProperties.put(
+                    DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN,
+                    FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN);
+            mPublicProperties.put(
+                    DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN,
+                    FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN);
+            mPublicProperties.put(
+                    PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                    FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+            mPublicProperties.put(
+                    PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+                    FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY);
+            mPublicProperties.put(
+                    PROPERTY_FEATURE_REAR_DISPLAY,
+                    FEATURE_REAR_DISPLAY);
+            mPublicProperties.put(
+                    PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT,
+                    FEATURE_DUAL_DISPLAY);
+        }
+
+        private final class HalBinderCallback implements IDeviceStateManagerCallback {
+            private final IDeviceStateListener mListener;
+
+            private HalBinderCallback(@NonNull IDeviceStateListener listener) {
+                mListener = listener;
+            }
+
+            @Override
+            public void onDeviceStateInfoChanged(DeviceStateInfo info) throws RemoteException {
+                DeviceStateConfiguration config = new DeviceStateConfiguration();
+                Set<Integer> systemProperties = new HashSet<>(
+                        info.currentState.getConfiguration().getSystemProperties());
+                Set<Integer> physicalProperties = new HashSet<>(
+                        info.currentState.getConfiguration().getPhysicalProperties());
+                config.deviceProperties = 0;
+                for (Integer prop : systemProperties) {
+                    Long publicProperty = mPublicProperties.get(prop);
+                    if (publicProperty != null) {
+                        config.deviceProperties |= publicProperty.longValue();
+                    }
+                }
+                for (Integer prop : physicalProperties) {
+                    Long publicProperty = mPublicProperties.get(prop);
+                    if (publicProperty != null) {
+                        config.deviceProperties |= publicProperty.longValue();
+                    }
+                }
+                mListener.onDeviceStateChanged(config);
+            }
+
+            @Override
+            public void onRequestActive(IBinder token) {
+                //No-op
+            }
+
+            @Override
+            public void onRequestCanceled(IBinder token) {
+                //No-op
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return mListener.asBinder();
+            }
+        }
+
+        @Override
+        public void registerListener(IDeviceStateListener listener) throws RemoteException {
+            if (listener == null) {
+                throw new ServiceSpecificException(ErrorCode.BAD_INPUT);
+            }
+
+            final int callingPid = Binder.getCallingPid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                HalBinderCallback callback = new HalBinderCallback(listener);
+                DeviceStateInfo info = registerProcess(callingPid, callback);
+                if (info != null)  {
+                    callback.onDeviceStateInfoChanged(info);
+                }
+            } catch (SecurityException e) {
+                throw new ServiceSpecificException(ErrorCode.ALREADY_EXISTS);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void unregisterListener(IDeviceStateListener listener) throws RemoteException {
+            final int callingPid = Binder.getCallingPid();
+
+            synchronized (mLock) {
+                if (mProcessRecords.contains(callingPid)) {
+                    mProcessRecords.remove(callingPid);
+                    return;
+                }
+            }
+
+            throw new ServiceSpecificException(ErrorCode.BAD_INPUT);
+        }
+
+        @Override
+        public int getInterfaceVersion() throws RemoteException {
+            return IDeviceStateService.VERSION;
+        }
+
+        @Override
+        public String getInterfaceHash() throws RemoteException {
+            return IDeviceStateService.HASH;
+        }
+    }
+
     /** Implementation of {@link IDeviceStateManager} published as a binder service. */
     private final class BinderService extends IDeviceStateManager.Stub {
 
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index e886f3b..2d2d258 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -221,6 +221,11 @@
         }
     }
 
+    @NonNull
+    private SystemMediaRoute2Provider getSystemProviderForUser(@NonNull UserHandler userHandler) {
+        return userHandler.mSystemProvider;
+    }
+
     // Start of methods that implement MediaRouter2 operations.
 
     @NonNull
@@ -246,7 +251,7 @@
                 UserRecord userRecord = getOrCreateUserRecordLocked(userId);
                 if (hasSystemRoutingPermissions) {
                     MediaRoute2ProviderInfo providerInfo =
-                            userRecord.mHandler.mSystemProvider.getProviderInfo();
+                            getSystemProviderForUser(userRecord.mHandler).getProviderInfo();
                     if (providerInfo != null) {
                         systemRoutes = providerInfo.getRoutes();
                     } else {
@@ -258,7 +263,8 @@
                     }
                 } else {
                     systemRoutes = new ArrayList<>();
-                    systemRoutes.add(userRecord.mHandler.mSystemProvider.getDefaultRoute());
+                    systemRoutes.add(
+                            getSystemProviderForUser(userRecord.mHandler).getDefaultRoute());
                 }
             }
             return new ArrayList<>(systemRoutes);
@@ -850,10 +856,11 @@
                     if (setDeviceRouteSelected) {
                         // Return a fake system session that shows the device route as selected and
                         // available bluetooth routes as transferable.
-                        return userRecord.mHandler.mSystemProvider
+                        return getSystemProviderForUser(userRecord.mHandler)
                                 .generateDeviceRouteSelectedSessionInfo(targetPackageName);
                     } else {
-                        sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
+                        sessionInfos = getSystemProviderForUser(userRecord.mHandler)
+                                .getSessionInfos();
                         if (!sessionInfos.isEmpty()) {
                             // Return a copy of the current system session with no modification,
                             // except setting the client package name.
@@ -866,7 +873,8 @@
                     }
                 } else {
                     return new RoutingSessionInfo.Builder(
-                                    userRecord.mHandler.mSystemProvider.getDefaultSessionInfo())
+                                    getSystemProviderForUser(userRecord.mHandler)
+                                            .getDefaultSessionInfo())
                             .setClientPackageName(targetPackageName)
                             .build();
                 }
@@ -1374,7 +1382,7 @@
             }
             manager.mLastSessionCreationRequest = null;
         } else {
-            String defaultRouteId = userHandler.mSystemProvider.getDefaultRoute().getId();
+            String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId();
             if (route.isSystemRoute()
                     && !routerRecord.hasSystemRoutingPermission()
                     && !TextUtils.equals(route.getId(), defaultRouteId)) {
@@ -1462,7 +1470,7 @@
                         routerRecord.mPackageName, routerRecord.mRouterId, route.getId()));
 
         UserHandler userHandler = routerRecord.mUserRecord.mHandler;
-        String defaultRouteId = userHandler.mSystemProvider.getDefaultRoute().getId();
+        String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId();
         if (route.isSystemRoute()
                 && !routerRecord.hasSystemRoutingPermission()
                 && !TextUtils.equals(route.getId(), defaultRouteId)) {
@@ -2125,11 +2133,12 @@
                 notifyRoutesUpdated(routesToReport.values().stream().toList());
 
                 List<RoutingSessionInfo> sessionInfos =
-                        mUserRecord.mHandler.mSystemProvider.getSessionInfos();
+                        getSystemProviderForUser(mUserRecord.mHandler).getSessionInfos();
                 RoutingSessionInfo systemSessionToReport =
                         newSystemRoutingPermissionValue && !sessionInfos.isEmpty()
                                 ? sessionInfos.get(0)
-                                : mUserRecord.mHandler.mSystemProvider.getDefaultSessionInfo();
+                                : getSystemProviderForUser(mUserRecord.mHandler)
+                                        .getDefaultSessionInfo();
                 notifySessionInfoChanged(systemSessionToReport);
             }
         }
@@ -2279,7 +2288,7 @@
                 if (route.isSystemRoute() && !hasSystemRoutingPermission()) {
                     // The router lacks permission to modify system routing, so we hide system
                     // route info from them.
-                    route = mUserRecord.mHandler.mSystemProvider.getDefaultRoute();
+                    route = getSystemProviderForUser(mUserRecord.mHandler).getDefaultRoute();
                 }
                 mRouter.requestCreateSessionByManager(uniqueRequestId, oldSession, route);
             } catch (RemoteException ex) {
@@ -2535,6 +2544,10 @@
 
         private boolean mRunning;
 
+        private SystemMediaRoute2Provider getSystemProvider() {
+            return mSystemProvider;
+        }
+
         // TODO: (In Android S+) Pull out SystemMediaRoute2Provider out of UserHandler.
         UserHandler(
                 @NonNull MediaRouter2ServiceImpl service,
@@ -2549,19 +2562,19 @@
                                     service.mContext, UserHandle.of(userRecord.mUserId), looper)
                             : new SystemMediaRoute2Provider(
                                     service.mContext, UserHandle.of(userRecord.mUserId), looper);
-            mRouteProviders.add(mSystemProvider);
+            mRouteProviders.add(getSystemProvider());
             mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
                     this, mUserRecord.mUserId);
         }
 
         void init() {
-            mSystemProvider.setCallback(this);
+            getSystemProvider().setCallback(this);
         }
 
         private void start() {
             if (!mRunning) {
                 mRunning = true;
-                mSystemProvider.start();
+                getSystemProvider().start();
                 mWatcher.start();
             }
         }
@@ -2570,7 +2583,7 @@
             if (mRunning) {
                 mRunning = false;
                 mWatcher.stop(); // also stops all providers
-                mSystemProvider.stop();
+                getSystemProvider().stop();
             }
         }
 
@@ -2662,7 +2675,7 @@
             String indent = prefix + "  ";
             pw.println(indent + "mRunning=" + mRunning);
 
-            mSystemProvider.dump(pw, prefix);
+            getSystemProvider().dump(pw, prefix);
             mWatcher.dump(pw, prefix);
         }
 
@@ -2755,7 +2768,7 @@
                     hasAddedOrModifiedRoutes,
                     hasRemovedRoutes,
                     provider.mIsSystemRouteProvider,
-                    mSystemProvider.getDefaultRoute());
+                    getSystemProvider().getDefaultRoute());
         }
 
         private static String getPackageNameFromNullableRecord(
@@ -2969,7 +2982,8 @@
             }
 
             // Bypass checking router if it's the system session (routerRecord should be null)
-            if (TextUtils.equals(getProviderId(uniqueSessionId), mSystemProvider.getUniqueId())) {
+            if (TextUtils.equals(
+                    getProviderId(uniqueSessionId), getSystemProvider().getUniqueId())) {
                 return true;
             }
 
@@ -3100,7 +3114,7 @@
                     && !matchingRequest.mRouterRecord.hasSystemRoutingPermission()) {
                 // The router lacks permission to modify system routing, so we hide system routing
                 // session info from them.
-                sessionInfo = mSystemProvider.getDefaultSessionInfo();
+                sessionInfo = getSystemProvider().getDefaultSessionInfo();
             }
             matchingRequest.mRouterRecord.notifySessionCreated(
                     toOriginalRequestId(uniqueRequestId), sessionInfo);
@@ -3114,13 +3128,13 @@
             }
 
             // For system provider, notify all routers.
-            if (provider == mSystemProvider) {
+            if (provider == getSystemProvider()) {
                 if (mServiceRef.get() == null) {
                     return;
                 }
                 notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo);
                 notifySessionInfoChangedToRouters(
-                        getRouterRecords(false), mSystemProvider.getDefaultSessionInfo());
+                        getRouterRecords(false), getSystemProvider().getDefaultSessionInfo());
                 return;
             }
 
@@ -3256,7 +3270,8 @@
             MediaRoute2ProviderInfo systemProviderInfo = null;
             for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
                 // TODO: Create MediaRoute2ProviderInfo#isSystemProvider()
-                if (TextUtils.equals(providerInfo.getUniqueId(), mSystemProvider.getUniqueId())) {
+                if (TextUtils.equals(
+                        providerInfo.getUniqueId(), getSystemProvider().getUniqueId())) {
                     // Adding routes from system provider will be handled below, so skip it here.
                     systemProviderInfo = providerInfo;
                     continue;
@@ -3272,10 +3287,10 @@
                     // This shouldn't happen.
                     Slog.wtf(TAG, "System route provider not found.");
                 }
-                currentSystemSessionInfo = mSystemProvider.getSessionInfos().get(0);
+                currentSystemSessionInfo = getSystemProvider().getSessionInfos().get(0);
             } else {
-                currentRoutes.add(mSystemProvider.getDefaultRoute());
-                currentSystemSessionInfo = mSystemProvider.getDefaultSessionInfo();
+                currentRoutes.add(getSystemProvider().getDefaultRoute());
+                currentSystemSessionInfo = getSystemProvider().getDefaultSessionInfo();
             }
 
             if (!currentRoutes.isEmpty()) {
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index c7e00d3..52433a56 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -25,7 +25,7 @@
 import android.media.quality.IMediaQualityManager;
 import android.media.quality.IPictureProfileCallback;
 import android.media.quality.ISoundProfileCallback;
-import android.media.quality.MediaQualityContract;
+import android.media.quality.MediaQualityContract.BaseParameters;
 import android.media.quality.ParamCapability;
 import android.media.quality.PictureProfile;
 import android.media.quality.PictureProfileHandle;
@@ -75,10 +75,10 @@
             SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
             ContentValues values = new ContentValues();
-            values.put(MediaQualityContract.BaseParameters.PARAMETER_TYPE, pp.getProfileType());
-            values.put(MediaQualityContract.BaseParameters.PARAMETER_NAME, pp.getName());
-            values.put(MediaQualityContract.BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
-            values.put(MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
+            values.put(BaseParameters.PARAMETER_TYPE, pp.getProfileType());
+            values.put(BaseParameters.PARAMETER_NAME, pp.getName());
+            values.put(BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
+            values.put(BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
             values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters()));
 
             // id is auto-generated by SQLite upon successful insertion of row
@@ -99,8 +99,8 @@
         public PictureProfile getPictureProfile(int type, String name) {
             SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
 
-            String selection = MediaQualityContract.BaseParameters.PARAMETER_TYPE + " = ? AND "
-                    + MediaQualityContract.BaseParameters.PARAMETER_NAME + " = ?";
+            String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+                    + BaseParameters.PARAMETER_NAME + " = ?";
             String[] selectionArguments = {Integer.toString(type), name};
 
             try (
@@ -176,26 +176,26 @@
 
         private String[] getAllPictureProfileColumns() {
             return new String[]{
-                    MediaQualityContract.BaseParameters.PARAMETER_ID,
-                    MediaQualityContract.BaseParameters.PARAMETER_TYPE,
-                    MediaQualityContract.BaseParameters.PARAMETER_NAME,
-                    MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID,
-                    MediaQualityContract.BaseParameters.PARAMETER_PACKAGE,
+                    BaseParameters.PARAMETER_ID,
+                    BaseParameters.PARAMETER_TYPE,
+                    BaseParameters.PARAMETER_NAME,
+                    BaseParameters.PARAMETER_INPUT_ID,
+                    BaseParameters.PARAMETER_PACKAGE,
                     mMediaQualityDbHelper.SETTINGS
             };
         }
 
         private PictureProfile getPictureProfileFromCursor(Cursor cursor) {
             String returnId = cursor.getString(cursor.getColumnIndexOrThrow(
-                    MediaQualityContract.BaseParameters.PARAMETER_ID));
+                    BaseParameters.PARAMETER_ID));
             int type = cursor.getInt(cursor.getColumnIndexOrThrow(
-                    MediaQualityContract.BaseParameters.PARAMETER_TYPE));
+                    BaseParameters.PARAMETER_TYPE));
             String name = cursor.getString(cursor.getColumnIndexOrThrow(
-                    MediaQualityContract.BaseParameters.PARAMETER_NAME));
+                    BaseParameters.PARAMETER_NAME));
             String inputId = cursor.getString(cursor.getColumnIndexOrThrow(
-                    MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID));
+                    BaseParameters.PARAMETER_INPUT_ID));
             String packageName = cursor.getString(cursor.getColumnIndexOrThrow(
-                    MediaQualityContract.BaseParameters.PARAMETER_PACKAGE));
+                    BaseParameters.PARAMETER_PACKAGE));
             String settings = cursor.getString(
                     cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS));
             return new PictureProfile(returnId, type, name, inputId,
@@ -204,7 +204,7 @@
 
         @Override
         public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
-            String selection = MediaQualityContract.BaseParameters.PARAMETER_PACKAGE + " = ?";
+            String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {packageName};
             return getPictureProfilesBasedOnConditions(getAllPictureProfileColumns(), selection,
                     selectionArguments);
@@ -217,7 +217,7 @@
 
         @Override
         public List<String> getPictureProfilePackageNames() {
-            String [] column = {MediaQualityContract.BaseParameters.PARAMETER_NAME};
+            String [] column = {BaseParameters.PARAMETER_NAME};
             List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
                     null, null);
             List<String> packageNames = new ArrayList<>();
@@ -255,18 +255,32 @@
         }
 
         @Override
-        public SoundProfile createSoundProfile(SoundProfile pp) {
-            // TODO: implement
-            return pp;
+        public SoundProfile createSoundProfile(SoundProfile sp) {
+            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+
+            ContentValues values = new ContentValues();
+            values.put(BaseParameters.PARAMETER_NAME, sp.getName());
+            values.put(BaseParameters.PARAMETER_PACKAGE, sp.getPackageName());
+            values.put(BaseParameters.PARAMETER_INPUT_ID, sp.getInputId());
+            values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(sp.getParameters()));
+
+            long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
+            return new SoundProfile.Builder(sp).setProfileId(Long.toString(id)).build();
         }
+
         @Override
-        public void updateSoundProfile(String id, SoundProfile pp) {
+        public void updateSoundProfile(String id, SoundProfile sp) {
             // TODO: implement
         }
+
         @Override
         public void removeSoundProfile(String id) {
-            // TODO: implement
+            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+            String selection = BaseParameters.PARAMETER_ID + " = ?";
+            String[] selectionArgs = {id};
+            db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, selectionArgs);
         }
+
         @Override
         public SoundProfile getSoundProfile(int type, String id) {
             return null;
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
index 27c4e9d..bc0fc2b 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
@@ -33,9 +33,10 @@
 
 public class BackgroundInstallControlCallbackHelper {
 
-    @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
-    @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId";
-    @VisibleForTesting static final String INSTALL_EVENT_TYPE_KEY = "installEventType";
+    public static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
+    public static final String FLAGGED_USER_ID_KEY = "userId";
+    public static final String INSTALL_EVENT_TYPE_KEY = "installEventType";
+
     private static final String TAG = "BackgroundInstallControlCallbackHelper";
 
     private final Handler mHandler;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d2546e4..ef33ffe 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3212,18 +3212,11 @@
      * will be ignored.
      */
     boolean isUniversalResizeable() {
-        if (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME) {
-            return false;
-        }
-        final boolean compatEnabled = Flags.universalResizableByDefault()
-                && mDisplayContent != null && mDisplayContent.getConfiguration()
-                    .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
-                && mDisplayContent.getIgnoreOrientationRequest()
-                && info.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
-        if (!compatEnabled && !mWmService.mConstants.mIgnoreActivityOrientationRequest) {
-            return false;
-        }
-        if (mWmService.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(packageName)) {
+        final boolean isLargeScreen = mDisplayContent != null && mDisplayContent.getConfiguration()
+                .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
+                && mDisplayContent.getIgnoreOrientationRequest();
+        if (!canBeUniversalResizeable(info.applicationInfo, mWmService, isLargeScreen,
+                true /* forActivity */)) {
             return false;
         }
         if (mAppCompatController.mAllowRestrictedResizability.getAsBoolean()) {
@@ -3234,6 +3227,31 @@
                 .userPreferenceCompatibleWithNonResizability();
     }
 
+    /**
+     * Returns {@code true} if the fixed orientation, aspect ratio, resizability of the application
+     * can be ignored.
+     */
+    static boolean canBeUniversalResizeable(ApplicationInfo appInfo, WindowManagerService wms,
+            boolean isLargeScreen, boolean forActivity) {
+        if (appInfo.category == ApplicationInfo.CATEGORY_GAME) {
+            return false;
+        }
+        final boolean compatEnabled = isLargeScreen && Flags.universalResizableByDefault()
+                && appInfo.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
+        if (!compatEnabled && !wms.mConstants.mIgnoreActivityOrientationRequest) {
+            return false;
+        }
+        if (wms.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(appInfo.packageName)) {
+            return false;
+        }
+        if (forActivity) {
+            // The caller will check both application and activity level property.
+            return true;
+        }
+        return !AppCompatController.allowRestrictedResizability(wms.mContext.getPackageManager(),
+                appInfo.packageName);
+    }
+
     boolean isResizeable() {
         return mAtmService.mForceResizableActivities
                 || ActivityInfo.isResizeableMode(info.resizeMode)
@@ -3667,16 +3685,6 @@
 
             pauseKeyDispatchingLocked();
 
-            // We are finishing the top focused activity and its task has nothing to be focused so
-            // the next focusable task should be focused.
-            if (mayAdjustTop && task.topRunningActivity(true /* focusableOnly */)
-                    == null) {
-                task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */,
-                            shouldAdjustGlobalFocus);
-            }
-
-            finishActivityResults(resultCode, resultData, resultGrants);
-
             final boolean endTask = task.getTopNonFinishingActivity() == null
                     && !task.isClearingToReuseTask();
             final WindowContainer<?> trigger = endTask ? task : this;
@@ -3687,6 +3695,16 @@
             if (transition != null) {
                 transition.collectClose(trigger);
             }
+            // We are finishing the top focused activity and its task has nothing to be focused so
+            // the next focusable task should be focused.
+            if (mayAdjustTop && task.topRunningActivity(true /* focusableOnly */)
+                    == null) {
+                task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */,
+                            shouldAdjustGlobalFocus);
+            }
+
+            finishActivityResults(resultCode, resultData, resultGrants);
+
             if (isState(RESUMED)) {
                 if (endTask) {
                     mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 203932d..330283f 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -77,13 +77,8 @@
                 mAppCompatOverrides);
         mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> {
             // Application level.
-            try {
-                if (packageManager.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY,
-                        mActivityRecord.packageName).getBoolean()) {
-                    return true;
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                // Fall through.
+            if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) {
+                return true;
             }
             // Activity level.
             try {
@@ -98,6 +93,15 @@
         });
     }
 
+    static boolean allowRestrictedResizability(PackageManager pm, String packageName) {
+        try {
+            return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName)
+                    .getBoolean();
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
     @NonNull
     TransparentPolicy getTransparentPolicy() {
         return mTransparentPolicy;
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index 114fe32..9457205 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -4,4 +4,9 @@
         <version>2</version>
         <fqname>IAltitudeService/default</fqname>
     </hal>
+    <hal format="aidl">
+        <name>android.frameworks.devicestate</name>
+        <version>1</version>
+        <fqname>IDeviceStateService/default</fqname>
+    </hal>
 </manifest>
diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
index ae78dfe..cc5be7e 100644
--- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -40,6 +41,7 @@
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemProperties;
@@ -50,6 +52,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.os.IBinaryTransparencyService;
+import com.android.server.pm.BackgroundInstallControlService;
+import com.android.server.pm.BackgroundInstallControlCallbackHelper;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageStateInternal;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -68,6 +76,9 @@
 public class BinaryTransparencyServiceTest {
     private static final String TAG = "BinaryTransparencyServiceTest";
 
+    private static final String TEST_PKG_NAME = "testPackageName";
+    private static final long TEST_VERSION_CODE = 1L;
+
     private Context mContext;
     private BinaryTransparencyService mBinaryTransparencyService;
     private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface;
@@ -83,6 +94,8 @@
     private PackageManager mPackageManager;
     @Mock
     private PackageManagerInternal mPackageManagerInternal;
+    @Mock
+    private BinaryTransparencyService.BicCallbackHandler.IBicAppInfoHelper mBicAppInfoHelper;
 
     @Captor
     private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback>
@@ -91,6 +104,9 @@
     private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback>
             mFaceAuthenticatorsRegisteredCaptor;
 
+    @Captor
+    private ArgumentCaptor<IBinaryTransparencyService.AppInfo> appInfoCaptor;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -262,4 +278,69 @@
                 eq("") /* softwareVersion */
         );
     }
+
+    @Test
+    public void BicCallbackHandler_uploads_mba_metrics() {
+        Bundle data = setupBicCallbackHandlerTest(false,
+            BinaryTransparencyService.MBA_STATUS_NEW_INSTALL);
+
+        BinaryTransparencyService.BicCallbackHandler handler =
+            new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper);
+        handler.sendResult(data);
+
+        verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture());
+        Assert.assertEquals(TEST_PKG_NAME, appInfoCaptor.getValue().packageName);
+        Assert.assertEquals(TEST_VERSION_CODE, appInfoCaptor.getValue().longVersion);
+    }
+
+    @Test
+    public void BicCallbackHandler_uploads_mba_metrics_for_preloads() {
+        Bundle data = setupBicCallbackHandlerTest(true,
+            BinaryTransparencyService.MBA_STATUS_UPDATED_PRELOAD);
+
+        BinaryTransparencyService.BicCallbackHandler handler =
+            new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper);
+        handler.sendResult(data);
+
+        verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture());
+        Assert.assertEquals(TEST_PKG_NAME, appInfoCaptor.getValue().packageName);
+        Assert.assertEquals(TEST_VERSION_CODE, appInfoCaptor.getValue().longVersion);
+    }
+
+    @Test
+    public void BicCallbackHandler_uploads_mba_metrics_for_uninstalls() {
+        Bundle data = new Bundle();
+        data.putString(BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY,
+            TEST_PKG_NAME);
+        data.putInt(BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY,
+            BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL);
+
+        BinaryTransparencyService.BicCallbackHandler handler =
+                new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper);
+        handler.sendResult(data);
+
+        verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture());
+        Assert.assertEquals(TEST_PKG_NAME ,appInfoCaptor.getValue().packageName);
+        Assert.assertEquals(BinaryTransparencyService.MBA_STATUS_UNINSTALLED,
+            appInfoCaptor.getValue().mbaStatus);
+    }
+
+    private Bundle setupBicCallbackHandlerTest(boolean isUpdatedSystemApp,
+            int expectedBtsMbaStatus) {
+        Bundle data = new Bundle();
+        data.putString(BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY,
+            TEST_PKG_NAME);
+        data.putInt(BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY,
+            BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL);
+        PackageStateInternal mockPackageState = mock(PackageStateInternal.class);
+        when(mPackageManagerInternal.getPackageStateInternal(TEST_PKG_NAME))
+            .thenReturn(mockPackageState);
+        when(mockPackageState.isUpdatedSystemApp()).thenReturn(isUpdatedSystemApp);
+        IBinaryTransparencyService.AppInfo appInfo = new IBinaryTransparencyService.AppInfo();
+        appInfo.packageName = TEST_PKG_NAME;
+        appInfo.longVersion = TEST_VERSION_CODE;
+        when(mBicAppInfoHelper.collectAppInfo(mockPackageState, expectedBtsMbaStatus))
+            .thenReturn(List.of(appInfo));
+        return data;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index ab5a5a9..5127b2d 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -24,9 +24,14 @@
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertThrows;
 
 import android.content.Context;
+import android.frameworks.devicestate.DeviceStateConfiguration;
+import android.frameworks.devicestate.ErrorCode;
+import android.frameworks.devicestate.IDeviceStateListener;
+import android.frameworks.devicestate.IDeviceStateService;
 import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.DeviceStateRequest;
@@ -34,6 +39,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -336,6 +342,53 @@
     }
 
     @Test
+    public void halRegisterUnregisterCallback() throws RemoteException {
+        IDeviceStateService halService = mService.getHalBinderService();
+        IDeviceStateListener halListener = new IDeviceStateListener.Stub() {
+            @Override
+            public void onDeviceStateChanged(DeviceStateConfiguration deviceState) { }
+
+            @Override
+            public int getInterfaceVersion() {
+                return IDeviceStateListener.VERSION;
+            }
+
+            @Override
+            public String getInterfaceHash() {
+                return IDeviceStateListener.HASH;
+            }
+        };
+
+        int errorCode = ErrorCode.OK;
+        try {
+            halService.unregisterListener(halListener);
+        } catch(ServiceSpecificException e) {
+            errorCode = e.errorCode;
+        }
+        assertEquals(errorCode, ErrorCode.BAD_INPUT);
+
+        errorCode = ErrorCode.OK;
+        try {
+            halService.unregisterListener(null);
+        } catch(ServiceSpecificException e) {
+            errorCode = e.errorCode;
+        }
+        assertEquals(errorCode, ErrorCode.BAD_INPUT);
+
+        halService.registerListener(halListener);
+
+        errorCode = ErrorCode.OK;
+        try {
+            halService.registerListener(halListener);
+        } catch (ServiceSpecificException e) {
+            errorCode = e.errorCode;
+        }
+        assertEquals(errorCode, ErrorCode.ALREADY_EXISTS);
+
+        halService.unregisterListener(halListener);
+    }
+
+    @Test
     public void registerCallback() throws RemoteException {
         final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 584c4c9..87db6c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4899,6 +4899,8 @@
         prepareLimitedBounds(mActivity, maxAspect, minAspect,
                 ActivityInfo.SCREEN_ORIENTATION_NOSENSOR, true /* isUnresizable */);
 
+        assertTrue(ActivityRecord.canBeUniversalResizeable(mActivity.info.applicationInfo,
+                mWm, true /* isLargeScreen */, false /* forActivity */));
         assertTrue(mActivity.isUniversalResizeable());
         assertTrue(mActivity.isResizeable());
         assertFalse(mActivity.shouldCreateAppCompatDisplayInsets());
@@ -4953,6 +4955,8 @@
                 .setComponent(getUniqueComponentName(mContext.getPackageName()))
                 .setTask(mTask).build();
         assertFalse(optOutAppActivity.isUniversalResizeable());
+        assertFalse(ActivityRecord.canBeUniversalResizeable(mActivity.info.applicationInfo,
+                mWm, true /* isLargeScreen */, false /* forActivity */));
     }
 
 
diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp
index e14e5fe..1c8386a 100644
--- a/tests/BinaryTransparencyHostTest/Android.bp
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -31,6 +31,8 @@
     ],
     static_libs: [
         "truth",
+        "flag-junit-host",
+        "android.app.flags-aconfig-java-host",
     ],
     device_common_data: [
         ":BinaryTransparencyTestApp",
diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
index 6e5f08a..6d8dbcb 100644
--- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
@@ -24,6 +24,9 @@
 
 import android.platform.test.annotations.LargeTest;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.host.HostFlagsValueProvider;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -34,6 +37,7 @@
 import com.android.tradefed.util.CommandStatus;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -49,6 +53,10 @@
     /** Waiting time for the job to be scheduled */
     private static final int JOB_CREATION_MAX_SECONDS = 30;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            HostFlagsValueProvider.createCheckFlagsRule(this::getDevice);
+
     @Before
     public void setUp() throws Exception {
         cancelPendingJob();
@@ -123,6 +131,7 @@
         }
     }
 
+    @RequiresFlagsDisabled(android.app.Flags.FLAG_BACKGROUND_INSTALL_CONTROL_CALLBACK_API)
     @Test
     public void testPreloadUpdateTriggersJobScheduling() throws Exception {
         try {