Merge "Add withLegacySuppressor helper method." into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8d480e5..de07fdde6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -16834,6 +16834,10 @@
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteCapabilities> CREATOR;
   }
 
+  @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteCapabilitiesCallback {
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilities);
+  }
+
   @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteDatagram implements android.os.Parcelable {
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public byte[] getSatelliteDatagram();
@@ -16852,6 +16856,7 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
@@ -16872,6 +16877,7 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 35509237..fd13174 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -575,8 +575,9 @@
                     extras.putString(Downloads.DIR_TYPE, dirType);
                     client.call(Downloads.CALL_CREATE_EXTERNAL_PUBLIC_DIR, null, extras);
                 } catch (RemoteException e) {
-                    throw new IllegalStateException("Unable to create directory: "
-                            + file.getAbsolutePath());
+                    throw new IllegalStateException(
+                        "Unable to create directory: " + file.getAbsolutePath(),
+                        e);
                 }
             } else {
                 if (file.exists()) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4c70c91..3df11f6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -81,6 +81,9 @@
 import android.app.IServiceConnection;
 import android.app.KeyguardManager;
 import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -9118,6 +9121,19 @@
     }
 
     /**
+     * For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above, the
+     * {@link #isDeviceOwnerApp} method will use the user contained within the
+     * context.
+     * For apps targeting an SDK version <em>below</em> this, the user of the calling process will
+     * be used (Process.myUserHandle()).
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public static final long IS_DEVICE_OWNER_USER_AWARE = 307233716L;
+
+    /**
      * Used to determine if a particular package has been registered as a Device Owner app.
      * A device owner app is a special device admin that cannot be deactivated by the user, once
      * activated as a device admin. It also cannot be uninstalled. To check whether a particular
@@ -9130,8 +9146,13 @@
      * app, if any.
      * @return whether or not the package is registered as the device owner app.
      */
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     public boolean isDeviceOwnerApp(String packageName) {
         throwIfParentInstance("isDeviceOwnerApp");
+        if (android.permission.flags.Flags.roleControllerInSystemServer()
+                && CompatChanges.isChangeEnabled(IS_DEVICE_OWNER_USER_AWARE)) {
+            return isDeviceOwnerAppOnContextUser(packageName);
+        }
         return isDeviceOwnerAppOnCallingUser(packageName);
     }
 
@@ -9192,6 +9213,24 @@
         return packageName.equals(deviceOwner.getPackageName());
     }
 
+    private boolean isDeviceOwnerAppOnContextUser(String packageName) {
+        if (packageName == null) {
+            return false;
+        }
+        ComponentName deviceOwner = null;
+        if (mService != null) {
+            try {
+                deviceOwner = mService.getDeviceOwnerComponentOnUser(myUserId());
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        if (deviceOwner == null) {
+            return false;
+        }
+        return packageName.equals(deviceOwner.getPackageName());
+    }
+
     private ComponentName getDeviceOwnerComponentInner(boolean callingUserOnly) {
         if (mService != null) {
             try {
@@ -9608,6 +9647,7 @@
      * @param packageName The package name of the app to compare with the registered profile owner.
      * @return Whether or not the package is registered as the profile owner.
      */
+    @UserHandleAware
     public boolean isProfileOwnerApp(String packageName) {
         throwIfParentInstance("isProfileOwnerApp");
         if (mService != null) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6fe40be..575fa4c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -179,6 +179,7 @@
 
     boolean setDeviceOwner(in ComponentName who, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
     ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
+    ComponentName getDeviceOwnerComponentOnUser(int userId);
     boolean hasDeviceOwner();
     String getDeviceOwnerName();
     void clearDeviceOwner(String packageName);
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index 543703e..a407704 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -190,9 +190,11 @@
         mSigningDetails.writeToParcel(dest, parcelableFlags);
     }
 
-    /* @hide */
+    /**
+     *  @hide
+     */
     @NonNull
-    SigningDetails getSigningDetails() {
+    public SigningDetails getSigningDetails() {
         return mSigningDetails;
     }
 
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 0df7c20..f24c3f5 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -1,5 +1,6 @@
 adamp@google.com
 asc@google.com
+austindelgado@google.com
 cinek@google.com
 dsandler@android.com
 dsandler@google.com
@@ -8,6 +9,7 @@
 hackbod@google.com
 ilyamaty@google.com
 jaggies@google.com
+jbolinger@google.com
 jsharkey@android.com
 jsharkey@google.com
 juliacr@google.com
diff --git a/media/java/android/media/tv/ad/TvAdServiceInfo.aidl b/media/java/android/media/tv/ad/TvAdServiceInfo.aidl
new file mode 100644
index 0000000..a5e631f
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.ad;
+
+parcelable TvAdServiceInfo;
diff --git a/media/java/android/media/tv/ad/TvAdServiceInfo.java b/media/java/android/media/tv/ad/TvAdServiceInfo.java
new file mode 100644
index 0000000..ed04f1f
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdServiceInfo.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.ad;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import androidx.annotation.NonNull;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to specify meta information of a TV AD service.
+ * @hide
+ */
+public final class TvAdServiceInfo implements Parcelable {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TvAdServiceInfo";
+
+    private static final String XML_START_TAG_NAME = "tv-ad-service";
+
+    private final ResolveInfo mService;
+    private final String mId;
+    private final List<String> mTypes = new ArrayList<>();
+
+    /**
+     * Constructs a TvAdServiceInfo object.
+     *
+     * @param context the application context
+     * @param component the component name of the TvAdService
+     */
+    public TvAdServiceInfo(@NonNull Context context, @NonNull ComponentName component) {
+        if (context == null) {
+            throw new IllegalArgumentException("context cannot be null.");
+        }
+        // TODO: use a constant
+        Intent intent = new Intent("android.media.tv.ad.TvAdService").setComponent(component);
+        ResolveInfo resolveInfo = context.getPackageManager().resolveService(
+                intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        if (resolveInfo == null) {
+            throw new IllegalArgumentException("Invalid component. Can't find the service.");
+        }
+
+        ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
+                resolveInfo.serviceInfo.name);
+        String id;
+        id = generateAdServiceId(componentName);
+        List<String> types = new ArrayList<>();
+        parseServiceMetadata(resolveInfo, context, types);
+
+        mService = resolveInfo;
+        mId = id;
+    }
+
+    private TvAdServiceInfo(ResolveInfo service, String id, List<String> types) {
+        mService = service;
+        mId = id;
+        mTypes.addAll(types);
+    }
+
+    private TvAdServiceInfo(@NonNull Parcel in) {
+        mService = ResolveInfo.CREATOR.createFromParcel(in);
+        mId = in.readString();
+        in.readStringList(mTypes);
+    }
+
+    public static final Creator<TvAdServiceInfo> CREATOR = new Creator<TvAdServiceInfo>() {
+        @Override
+        public TvAdServiceInfo createFromParcel(Parcel in) {
+            return new TvAdServiceInfo(in);
+        }
+
+        @Override
+        public TvAdServiceInfo[] newArray(int size) {
+            return new TvAdServiceInfo[size];
+        }
+    };
+
+    /**
+     * Returns a unique ID for this TV AD service. The ID is generated from the package and class
+     * name implementing the TV AD service.
+     */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the component of the TV AD service.
+     * @hide
+     */
+    public ComponentName getComponent() {
+        return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
+    }
+
+    /**
+     * Returns the information of the service that implements this AD service.
+     */
+    @Nullable
+    public ServiceInfo getServiceInfo() {
+        return mService.serviceInfo;
+    }
+
+    /**
+     * Gets supported TV AD types.
+     */
+    @NonNull
+    public List<String> getSupportedTypes() {
+        return mTypes;
+    }
+
+    private static String generateAdServiceId(ComponentName name) {
+        return name.flattenToShortString();
+    }
+
+    private static void parseServiceMetadata(
+            ResolveInfo resolveInfo, Context context, List<String> types) {
+        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+        PackageManager pm = context.getPackageManager();
+        // TODO: use constant for the metadata
+        try (XmlResourceParser parser =
+                     serviceInfo.loadXmlMetaData(pm, "android.media.tv.ad.service")) {
+            if (parser == null) {
+                throw new IllegalStateException(
+                        "No " + "android.media.tv.ad.service"
+                                + " meta-data found for " + serviceInfo.name);
+            }
+
+            Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+                // move to the START_TAG
+            }
+
+            String nodeName = parser.getName();
+            if (!XML_START_TAG_NAME.equals(nodeName)) {
+                throw new IllegalStateException("Meta-data does not start with "
+                        + XML_START_TAG_NAME + " tag for " + serviceInfo.name);
+            }
+
+            // TODO: parse attributes
+        } catch (IOException | XmlPullParserException e) {
+            throw new IllegalStateException(
+                    "Failed reading meta-data for " + serviceInfo.packageName, e);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("No resources found for " + serviceInfo.packageName, e);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mService.writeToParcel(dest, flags);
+        dest.writeString(mId);
+        dest.writeStringList(mTypes);
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 33c084e..3928767 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -78,7 +78,10 @@
 import androidx.compose.ui.unit.times
 import com.android.compose.PlatformButton
 import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneKey as SceneTransitionLayoutSceneKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.transitions
 import com.android.compose.modifiers.thenIf
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
@@ -90,6 +93,8 @@
 import com.android.systemui.common.shared.model.Text.Companion.loadText
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.fold.ui.composable.FoldPosture
+import com.android.systemui.fold.ui.composable.foldPosture
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.SceneKey
@@ -159,28 +164,27 @@
 
         when (layout) {
             Layout.STANDARD ->
-                Bouncer(
+                StandardLayout(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
-                    userInputAreaVisibility = UserInputAreaVisibility.FULL,
                     modifier = childModifier,
                 )
             Layout.SIDE_BY_SIDE ->
-                SideBySide(
+                SideBySideLayout(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
                     isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
                     modifier = childModifier,
                 )
             Layout.STACKED ->
-                Stacked(
+                StackedLayout(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
                     isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
                     modifier = childModifier,
                 )
             Layout.SPLIT ->
-                Split(
+                SplitLayout(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
                     modifier = childModifier,
@@ -194,59 +198,150 @@
  * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
  */
 @Composable
-private fun Bouncer(
+private fun StandardLayout(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerSceneDialogFactory,
-    userInputAreaVisibility: UserInputAreaVisibility,
+    modifier: Modifier = Modifier,
+    outputOnly: Boolean = false,
+) {
+    val foldPosture: FoldPosture by foldPosture()
+    val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
+    val isSplitAroundTheFold =
+        foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired
+    val currentSceneKey by
+        remember(isSplitAroundTheFold) {
+            mutableStateOf(
+                if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
+            )
+        }
+
+    SceneTransitionLayout(
+        currentScene = currentSceneKey,
+        onChangeScene = {},
+        transitions = SceneTransitions,
+        modifier = modifier,
+    ) {
+        scene(SceneKeys.ContiguousSceneKey) {
+            FoldSplittable(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                outputOnly = outputOnly,
+                isSplit = false,
+            )
+        }
+
+        scene(SceneKeys.SplitSceneKey) {
+            FoldSplittable(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                outputOnly = outputOnly,
+                isSplit = true,
+            )
+        }
+    }
+}
+
+/**
+ * Renders the "standard" layout of the bouncer, where the bouncer is rendered on its own (no user
+ * switcher UI) and laid out vertically, centered horizontally.
+ *
+ * If [isSplit] is `true`, the top and bottom parts of the bouncer are split such that they don't
+ * render across the location of the fold hardware when the device is fully or part-way unfolded
+ * with the fold hinge in a horizontal position.
+ *
+ * If [outputOnly] is `true`, only the "output" part of the UI is shown (where the entered PIN
+ * "shapes" appear), if `false`, the entire UI is shown, including the area where the user can enter
+ * their PIN or pattern.
+ */
+@Composable
+private fun SceneScope.FoldSplittable(
+    viewModel: BouncerViewModel,
+    dialogFactory: BouncerSceneDialogFactory,
+    outputOnly: Boolean,
+    isSplit: Boolean,
     modifier: Modifier = Modifier,
 ) {
     val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
     val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
     var dialog: Dialog? by remember { mutableStateOf(null) }
     val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
+    val splitRatio =
+        LocalContext.current.resources.getFloat(
+            R.dimen.motion_layout_half_fold_bouncer_height_ratio
+        )
 
-    Column(
-        horizontalAlignment = Alignment.CenterHorizontally,
-        modifier = modifier.padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 0.dp)
-    ) {
-        Crossfade(
-            targetState = message,
-            label = "Bouncer message",
-            animationSpec = if (message.isUpdateAnimated) tween() else snap(),
-        ) { message ->
-            Text(
-                text = message.text,
-                color = MaterialTheme.colorScheme.onSurface,
-                style = MaterialTheme.typography.bodyLarge,
-            )
-        }
+    Column(modifier = modifier.padding(horizontal = 32.dp)) {
+        // Content above the fold, when split on a foldable device in a "table top" posture:
+        Box(
+            modifier =
+                Modifier.element(SceneElements.AboveFold).fillMaxWidth().thenIf(isSplit) {
+                    Modifier.weight(splitRatio)
+                },
+        ) {
+            Column(
+                horizontalAlignment = Alignment.CenterHorizontally,
+                modifier = Modifier.fillMaxWidth().padding(top = 92.dp),
+            ) {
+                Crossfade(
+                    targetState = message,
+                    label = "Bouncer message",
+                    animationSpec = if (message.isUpdateAnimated) tween() else snap(),
+                ) { message ->
+                    Text(
+                        text = message.text,
+                        color = MaterialTheme.colorScheme.onSurface,
+                        style = MaterialTheme.typography.bodyLarge,
+                    )
+                }
 
-        Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+                Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
 
-        Box(Modifier.weight(1f)) {
-            UserInputArea(
-                viewModel = viewModel,
-                visibility = userInputAreaVisibility,
-                modifier = Modifier.align(Alignment.Center),
-            )
-        }
-
-        Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
-
-        val actionButtonModifier = Modifier.height(56.dp)
-
-        actionButton.let { actionButtonViewModel ->
-            if (actionButtonViewModel != null) {
-                BouncerActionButton(
-                    viewModel = actionButtonViewModel,
-                    modifier = actionButtonModifier,
+                UserInputArea(
+                    viewModel = viewModel,
+                    visibility = UserInputAreaVisibility.OUTPUT_ONLY,
                 )
-            } else {
-                Spacer(modifier = actionButtonModifier)
             }
         }
 
-        Spacer(Modifier.height(48.dp))
+        // Content below the fold, when split on a foldable device in a "table top" posture:
+        Box(
+            modifier =
+                Modifier.element(SceneElements.BelowFold).fillMaxWidth().thenIf(isSplit) {
+                    Modifier.weight(1 - splitRatio)
+                },
+        ) {
+            Column(
+                horizontalAlignment = Alignment.CenterHorizontally,
+                modifier = Modifier.fillMaxWidth(),
+            ) {
+                if (!outputOnly) {
+                    Box(Modifier.weight(1f)) {
+                        UserInputArea(
+                            viewModel = viewModel,
+                            visibility = UserInputAreaVisibility.INPUT_ONLY,
+                            modifier = Modifier.align(Alignment.Center),
+                        )
+                    }
+                }
+
+                Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+
+                val actionButtonModifier = Modifier.height(56.dp)
+
+                actionButton.let { actionButtonViewModel ->
+                    if (actionButtonViewModel != null) {
+                        BouncerActionButton(
+                            viewModel = actionButtonViewModel,
+                            modifier = actionButtonModifier,
+                        )
+                    } else {
+                        Spacer(modifier = actionButtonModifier)
+                    }
+                }
+
+                Spacer(Modifier.height(48.dp))
+            }
+        }
 
         if (dialogMessage != null) {
             if (dialog == null) {
@@ -288,8 +383,8 @@
     when (val nonNullViewModel = authMethodViewModel) {
         is PinBouncerViewModel ->
             when (visibility) {
-                UserInputAreaVisibility.FULL ->
-                    PinBouncer(
+                UserInputAreaVisibility.OUTPUT_ONLY ->
+                    PinInputDisplay(
                         viewModel = nonNullViewModel,
                         modifier = modifier,
                     )
@@ -298,34 +393,21 @@
                         viewModel = nonNullViewModel,
                         modifier = modifier,
                     )
-                UserInputAreaVisibility.OUTPUT_ONLY ->
-                    PinInputDisplay(
-                        viewModel = nonNullViewModel,
-                        modifier = modifier,
-                    )
-                UserInputAreaVisibility.NONE -> {}
             }
         is PasswordBouncerViewModel ->
-            when (visibility) {
-                UserInputAreaVisibility.FULL,
-                UserInputAreaVisibility.INPUT_ONLY ->
-                    PasswordBouncer(
-                        viewModel = nonNullViewModel,
-                        modifier = modifier,
-                    )
-                else -> {}
+            if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
+                PasswordBouncer(
+                    viewModel = nonNullViewModel,
+                    modifier = modifier,
+                )
             }
         is PatternBouncerViewModel ->
-            when (visibility) {
-                UserInputAreaVisibility.FULL,
-                UserInputAreaVisibility.INPUT_ONLY ->
-                    PatternBouncer(
-                        viewModel = nonNullViewModel,
-                        modifier =
-                            Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
-                                .then(modifier)
-                    )
-                else -> {}
+            if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
+                PatternBouncer(
+                    viewModel = nonNullViewModel,
+                    modifier =
+                        Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier)
+                )
             }
         else -> Unit
     }
@@ -492,17 +574,17 @@
  * by double-tapping on the side.
  */
 @Composable
-private fun Split(
+private fun SplitLayout(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerSceneDialogFactory,
     modifier: Modifier = Modifier,
 ) {
     SwappableLayout(
         startContent = { startContentModifier ->
-            Bouncer(
+            StandardLayout(
                 viewModel = viewModel,
                 dialogFactory = dialogFactory,
-                userInputAreaVisibility = UserInputAreaVisibility.OUTPUT_ONLY,
+                outputOnly = true,
                 modifier = startContentModifier,
             )
         },
@@ -595,7 +677,7 @@
  * rendering of the bouncer will be used instead of the side-by-side layout.
  */
 @Composable
-private fun SideBySide(
+private fun SideBySideLayout(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerSceneDialogFactory,
     isUserSwitcherVisible: Boolean,
@@ -615,10 +697,9 @@
             }
         },
         endContent = { endContentModifier ->
-            Bouncer(
+            StandardLayout(
                 viewModel = viewModel,
                 dialogFactory = dialogFactory,
-                userInputAreaVisibility = UserInputAreaVisibility.FULL,
                 modifier = endContentModifier,
             )
         },
@@ -628,7 +709,7 @@
 
 /** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
 @Composable
-private fun Stacked(
+private fun StackedLayout(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerSceneDialogFactory,
     isUserSwitcherVisible: Boolean,
@@ -644,10 +725,9 @@
             )
         }
 
-        Bouncer(
+        StandardLayout(
             viewModel = viewModel,
             dialogFactory = dialogFactory,
-            userInputAreaVisibility = UserInputAreaVisibility.FULL,
             modifier = Modifier.fillMaxWidth().weight(1f),
         )
     }
@@ -708,11 +788,6 @@
 /** Enumerates all supported user-input area visibilities. */
 private enum class UserInputAreaVisibility {
     /**
-     * The entire user input area is shown, including where the user enters input and where it's
-     * reflected to the user.
-     */
-    FULL,
-    /**
      * Only the area where the user enters the input is shown; the area where the input is reflected
      * back to the user is not shown.
      */
@@ -722,8 +797,6 @@
      * input is entered by the user is not shown.
      */
     OUTPUT_ONLY,
-    /** The entire user input area is hidden. */
-    NONE,
 }
 
 /**
@@ -758,3 +831,17 @@
 private val SelectedUserImageSize = 190.dp
 private val UserSwitcherDropdownWidth = SelectedUserImageSize + 2 * 29.dp
 private val UserSwitcherDropdownHeight = 60.dp
+
+private object SceneKeys {
+    val ContiguousSceneKey = SceneTransitionLayoutSceneKey("default")
+    val SplitSceneKey = SceneTransitionLayoutSceneKey("split")
+}
+
+private object SceneElements {
+    val AboveFold = ElementKey("above_fold")
+    val BelowFold = ElementKey("below_fold")
+}
+
+private val SceneTransitions = transitions {
+    from(SceneKeys.ContiguousSceneKey, to = SceneKeys.SplitSceneKey) { spec = tween() }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 5b9ad4d..fb50f69 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -24,14 +24,10 @@
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.sizeIn
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -69,34 +65,13 @@
 import kotlinx.coroutines.launch
 
 @Composable
-internal fun PinBouncer(
+fun PinPad(
     viewModel: PinBouncerViewModel,
     modifier: Modifier = Modifier,
 ) {
     // Report that the UI is shown to let the view-model run some logic.
     LaunchedEffect(Unit) { viewModel.onShown() }
 
-    Column(
-        horizontalAlignment = Alignment.CenterHorizontally,
-        modifier =
-            modifier.pointerInput(Unit) {
-                awaitEachGesture {
-                    awaitFirstDown()
-                    viewModel.onDown()
-                }
-            }
-    ) {
-        PinInputDisplay(viewModel)
-        Spacer(Modifier.heightIn(min = 34.dp, max = 48.dp))
-        PinPad(viewModel)
-    }
-}
-
-@Composable
-fun PinPad(
-    viewModel: PinBouncerViewModel,
-    modifier: Modifier = Modifier,
-) {
     val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
     val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
     val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
@@ -298,7 +273,8 @@
         contentAlignment = Alignment.Center,
         modifier =
             modifier
-                .size(pinButtonSize)
+                .sizeIn(maxWidth = pinButtonSize, maxHeight = pinButtonSize)
+                .aspectRatio(1f)
                 .drawBehind {
                     drawRoundRect(
                         color = containerColor,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
new file mode 100644
index 0000000..1c993cf
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.fold.ui.composable
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.produceState
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.WindowInfoTracker
+
+sealed interface FoldPosture {
+    /** A foldable device that's fully closed/folded or a device that doesn't support folding. */
+    data object Folded : FoldPosture
+    /** A foldable that's halfway open with the hinge held vertically. */
+    data object Book : FoldPosture
+    /** A foldable that's halfway open with the hinge held horizontally. */
+    data object Tabletop : FoldPosture
+    /** A foldable that's fully unfolded / flat. */
+    data object FullyUnfolded : FoldPosture
+}
+
+/** Returns the [FoldPosture] of the device currently. */
+@Composable
+fun foldPosture(): State<FoldPosture> {
+    val context = LocalContext.current
+    val infoTracker = remember(context) { WindowInfoTracker.getOrCreate(context) }
+    val layoutInfo by infoTracker.windowLayoutInfo(context).collectAsState(initial = null)
+
+    return produceState<FoldPosture>(
+        initialValue = FoldPosture.Folded,
+        key1 = layoutInfo,
+    ) {
+        value =
+            layoutInfo
+                ?.displayFeatures
+                ?.firstNotNullOfOrNull { it as? FoldingFeature }
+                .let { foldingFeature ->
+                    when (foldingFeature?.state) {
+                        null -> FoldPosture.Folded
+                        FoldingFeature.State.HALF_OPENED ->
+                            foldingFeature.orientation.toHalfwayPosture()
+                        FoldingFeature.State.FLAT ->
+                            if (foldingFeature.isSeparating) {
+                                // Dual screen device.
+                                foldingFeature.orientation.toHalfwayPosture()
+                            } else {
+                                FoldPosture.FullyUnfolded
+                            }
+                        else -> error("Unsupported state \"${foldingFeature.state}\"")
+                    }
+                }
+    }
+}
+
+private fun FoldingFeature.Orientation.toHalfwayPosture(): FoldPosture {
+    return when (this) {
+        FoldingFeature.Orientation.HORIZONTAL -> FoldPosture.Tabletop
+        FoldingFeature.Orientation.VERTICAL -> FoldPosture.Book
+        else -> error("Unsupported orientation \"$this\"")
+    }
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8780f58..9a3c6d5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3266,4 +3266,9 @@
     <string name="privacy_dialog_active_app_usage_2">In use by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string>
     <!-- Label for recent app usage of a phone sensor with sub-attribution and proxy label in the privacy dialog [CHAR LIMIT=NONE] -->
     <string name="privacy_dialog_recent_app_usage_2">Recently used by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string>
+
+    <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
+    <string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
+    <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] -->
+    <string name="keyboard_backlight_value">Level %1$d of %2$d</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 80be008..e8b16db 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -65,7 +65,7 @@
      * Note that the length of the PIN is also important to take into consideration, please see
      * [hintedPinLength].
      */
-    val isAutoConfirmEnabled: StateFlow<Boolean>
+    val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
 
     /**
      * The exact length a PIN should be for us to enable PIN length hinting.
@@ -152,14 +152,14 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
     private val userRepository: UserRepository,
     private val lockPatternUtils: LockPatternUtils,
     broadcastDispatcher: BroadcastDispatcher,
 ) : AuthenticationRepository {
 
-    override val isAutoConfirmEnabled: StateFlow<Boolean> =
+    override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
         refreshingFlow(
             initialValue = false,
             getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled,
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 345f15c..22b44d5 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -42,6 +42,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
@@ -103,9 +104,29 @@
                 initialValue = throttling.value.remainingMs > 0,
             )
 
+    /**
+     * Whether the auto confirm feature is enabled for the currently-selected user.
+     *
+     * Note that the length of the PIN is also important to take into consideration, please see
+     * [hintedPinLength].
+     *
+     * During throttling, this is always disabled (`false`).
+     */
+    val isAutoConfirmEnabled: StateFlow<Boolean> =
+        combine(repository.isAutoConfirmFeatureEnabled, isThrottled) { featureEnabled, isThrottled
+                ->
+                // Disable auto-confirm during throttling.
+                featureEnabled && !isThrottled
+            }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
+
     /** The length of the hinted PIN, or `null` if pin length hint should not be shown. */
     val hintedPinLength: StateFlow<Int?> =
-        repository.isAutoConfirmEnabled
+        isAutoConfirmEnabled
             .map { isAutoConfirmEnabled ->
                 repository.getPinLength().takeIf {
                     isAutoConfirmEnabled && it == repository.hintedPinLength
@@ -119,9 +140,6 @@
                 initialValue = null,
             )
 
-    /** Whether the auto confirm feature is enabled for the currently-selected user. */
-    val isAutoConfirmEnabled: StateFlow<Boolean> = repository.isAutoConfirmEnabled
-
     /** Whether the pattern should be visible for the currently-selected user. */
     val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 73d15f0..4767e21 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -180,6 +180,19 @@
                 initialValue = isSideBySideSupported(authMethodViewModel.value),
             )
 
+    /**
+     * Whether the splitting the UI around the fold seam (where the hinge is on a foldable device)
+     * is required.
+     */
+    val isFoldSplitRequired: StateFlow<Boolean> =
+        authMethodViewModel
+            .map { authMethod -> isFoldSplitRequired(authMethod) }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = isFoldSplitRequired(authMethodViewModel.value),
+            )
+
     init {
         if (flags.isEnabled()) {
             applicationScope.launch {
@@ -212,6 +225,10 @@
         return isUserSwitcherVisible || authMethod !is PasswordBouncerViewModel
     }
 
+    private fun isFoldSplitRequired(authMethod: AuthMethodBouncerViewModel?): Boolean {
+        return authMethod !is PasswordBouncerViewModel
+    }
+
     private fun toMessageViewModel(
         message: String?,
         isThrottled: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index 6b27ce0..f7fee96 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -18,13 +18,11 @@
 
 import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetManager
-import android.appwidget.AppWidgetProviderInfo
 import android.content.BroadcastReceiver
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
-import android.content.pm.PackageManager
 import android.os.UserManager
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -32,13 +30,10 @@
 import com.android.systemui.communal.data.db.CommunalWidgetDao
 import com.android.systemui.communal.data.db.CommunalWidgetItem
 import com.android.systemui.communal.shared.CommunalWidgetHost
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
@@ -58,9 +53,6 @@
 
 /** Encapsulates the state of widgets for communal mode. */
 interface CommunalWidgetRepository {
-    /** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
-    val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
-
     /** A flow of information about active communal widgets stored in database. */
     val communalWidgets: Flow<List<CommunalWidgetContentModel>>
 
@@ -84,15 +76,12 @@
     communalRepository: CommunalRepository,
     private val communalWidgetHost: CommunalWidgetHost,
     private val communalWidgetDao: CommunalWidgetDao,
-    private val packageManager: PackageManager,
     private val userManager: UserManager,
     private val userTracker: UserTracker,
     @CommunalLog logBuffer: LogBuffer,
-    featureFlags: FeatureFlagsClassic,
 ) : CommunalWidgetRepository {
     companion object {
         const val TAG = "CommunalWidgetRepository"
-        const val WIDGET_LABEL = "Stopwatch"
     }
 
     private val logger = Logger(logBuffer, TAG)
@@ -100,9 +89,6 @@
     // Whether the [AppWidgetHost] is listening for updates.
     private var isHostListening = false
 
-    // Widgets that should be rendered in communal mode.
-    private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
-
     private val isUserUnlocked: Flow<Boolean> =
         callbackFlow {
                 if (!communalRepository.isCommunalEnabled) {
@@ -149,25 +135,6 @@
             }
         }
 
-    override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> =
-        isHostActive.map { isHostActive ->
-            if (!isHostActive || !featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
-                return@map null
-            }
-
-            val providerInfo =
-                appWidgetManager.installedProviders.find {
-                    it.loadLabel(packageManager).equals(WIDGET_LABEL)
-                }
-
-            if (providerInfo == null) {
-                logger.w("Cannot find app widget: $WIDGET_LABEL")
-                return@map null
-            }
-
-            return@map addStopWatchWidget(providerInfo)
-        }
-
     override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
         isHostActive.flatMapLatest { isHostActive ->
             if (!isHostActive) {
@@ -226,21 +193,4 @@
         appWidgetHost.stopListening()
         isHostListening = false
     }
-
-    // TODO(b/306471933): remove this prototype that shows a stopwatch in the communal blueprint
-    private fun addStopWatchWidget(providerInfo: AppWidgetProviderInfo): CommunalAppWidgetInfo {
-        val existing = widgets.values.firstOrNull { it.providerInfo == providerInfo }
-        if (existing != null) {
-            return existing
-        }
-
-        val appWidgetId = appWidgetHost.allocateAppWidgetId()
-        val widget =
-            CommunalAppWidgetInfo(
-                providerInfo,
-                appWidgetId,
-            )
-        widgets[appWidgetId] = widget
-        return widget
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index eb36b19..508f52c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.communal.data.repository.CommunalRepository
 import com.android.systemui.communal.data.repository.CommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.dagger.SysUISingleton
@@ -55,9 +54,6 @@
     val isCommunalEnabled: Boolean
         get() = communalRepository.isCommunalEnabled
 
-    /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
-    val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
-
     /**
      * Target scene as requested by the underlying [SceneTransitionLayout] or through
      * [onSceneChanged].
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
deleted file mode 100644
index 65bf4b3..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.ui.binder
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.launch
-
-/** Binds [CommunalWidgetViewModel] to the keyguard root view. */
-object CommunalWidgetViewBinder {
-
-    @JvmStatic
-    fun bind(
-        rootView: KeyguardRootView,
-        viewModel: CommunalWidgetViewModel,
-        adapter: CommunalWidgetViewAdapter,
-        keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
-    ) {
-        rootView.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch {
-                    adapter.adapt(viewModel.appWidgetInfo).collect {
-                        val oldView =
-                            rootView.findViewById<CommunalWidgetWrapper>(
-                                R.id.communal_widget_wrapper
-                            )
-                        var dirty = false
-
-                        if (oldView != null) {
-                            rootView.removeView(oldView)
-                            dirty = true
-                        }
-
-                        if (it != null) {
-                            rootView.addView(it)
-                            dirty = true
-                        }
-
-                        if (dirty) {
-                            keyguardBlueprintInteractor.refreshBlueprint()
-                        }
-                    }
-                }
-
-                launch { viewModel.alpha.collect { rootView.alpha = it } }
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index 702554a..9198c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.communal.ui.view.layout.blueprints
 
 import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
-import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
 import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -30,13 +29,11 @@
 @Inject
 constructor(
     defaultCommunalHubSection: DefaultCommunalHubSection,
-    defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
 ) : KeyguardBlueprint {
     override val id: String = COMMUNAL
     override val sections: List<KeyguardSection> =
         listOf(
             defaultCommunalHubSection,
-            defaultCommunalWidgetSection,
         )
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
deleted file mode 100644
index c3e8a96..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.ui.view.layout.sections
-
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import com.android.systemui.res.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import dagger.Lazy
-import javax.inject.Inject
-
-class DefaultCommunalWidgetSection
-@Inject
-constructor(
-    private val featureFlags: FeatureFlags,
-    private val keyguardRootView: KeyguardRootView,
-    private val communalWidgetViewModel: CommunalWidgetViewModel,
-    private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
-    private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
-) : KeyguardSection() {
-    private val widgetAreaViewId = R.id.communal_widget_wrapper
-
-    override fun addViews(constraintLayout: ConstraintLayout) {}
-
-    override fun bindData(constraintLayout: ConstraintLayout) {
-        if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
-            return
-        }
-
-        CommunalWidgetViewBinder.bind(
-            keyguardRootView,
-            communalWidgetViewModel,
-            communalWidgetViewAdapter,
-            keyguardBlueprintInteractor.get(),
-        )
-    }
-
-    override fun applyConstraints(constraintSet: ConstraintSet) {
-        constraintSet.apply {
-            constrainWidth(widgetAreaViewId, WRAP_CONTENT)
-            constrainHeight(widgetAreaViewId, WRAP_CONTENT)
-            connect(widgetAreaViewId, BOTTOM, PARENT_ID, BOTTOM)
-            connect(widgetAreaViewId, END, PARENT_ID, END)
-        }
-    }
-
-    override fun removeViews(constraintLayout: ConstraintLayout) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
deleted file mode 100644
index d7bbea6..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.ui.viewmodel
-
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-
-@SysUISingleton
-class CommunalWidgetViewModel
-@Inject
-constructor(
-    communalInteractor: CommunalInteractor,
-    keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
-) {
-    /** An observable for the alpha level for the communal widget area. */
-    val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
-
-    /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
-    val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = communalInteractor.appWidgetInfo
-}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b237b87..d64a131 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -239,8 +239,7 @@
 
     /** Provide new auth messages on the bouncer. */
     // TODO(b/277961132): Tracking bug.
-    @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages",
-            teamfood = true)
+    @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
 
     /** Keyguard Migration */
 
@@ -255,9 +254,6 @@
     // TODO(b/287268101): Tracking bug.
     @JvmField val TRANSIT_CLOCK = releasedFlag("lockscreen_custom_transit_clock")
 
-    // TODO(b/288276738): Tracking bug.
-    @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
-
     /** Migrate the NSSL to the a sibling to both the panel and keyguard root view. */
     // TODO(b/288074305): Tracking bug.
     @JvmField val MIGRATE_NSSL = unreleasedFlag("migrate_nssl")
@@ -776,9 +772,4 @@
     @JvmField
     val COMMUNAL_SERVICE_ENABLED = resourceBooleanFlag(R.bool.config_communalServiceEnabled,
         "communal_service_enabled")
-
-    // TODO(b/303131306): Tracking Bug
-    /** Whether communal hub features are enabled. */
-    @JvmField
-    val COMMUNAL_HUB = unreleasedFlag("communal_hub")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
index e16bb0b..1e9be09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -30,6 +30,7 @@
 import android.view.ViewGroup.MarginLayoutParams
 import android.view.Window
 import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
 import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
@@ -78,23 +79,29 @@
     private lateinit var stepProperties: StepViewProperties
 
     @ColorInt
-    var filledRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
-    @ColorInt
-    var emptyRectangleColor =
-        getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant)
-    @ColorInt
-    var backgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
-    @ColorInt
-    var defaultIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
-    @ColorInt
-    var defaultIconBackgroundColor =
+    private val filledRectangleColor =
         getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
     @ColorInt
-    var dimmedIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
+    private val emptyRectangleColor =
+        getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant)
     @ColorInt
-    var dimmedIconBackgroundColor =
+    private val backgroundColor =
+        getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
+    @ColorInt
+    private val defaultIconColor =
+        getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
+    @ColorInt
+    private val defaultIconBackgroundColor =
+        getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
+    @ColorInt
+    private val dimmedIconColor =
+        getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
+    @ColorInt
+    private val dimmedIconBackgroundColor =
         getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceDim)
 
+    private val levelContentDescription = context.getString(R.string.keyboard_backlight_value)
+
     init {
         currentLevel = initialCurrentLevel
         maxLevel = initialMaxLevel
@@ -103,6 +110,8 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         setUpWindowProperties(this)
         setWindowPosition()
+        // title is used for a11y announcement
+        window?.setTitle(context.getString(R.string.keyboard_backlight_dialog_title))
         updateResources()
         rootView = buildRootView()
         setContentView(rootView)
@@ -159,6 +168,12 @@
         currentLevel = current
         updateIconTile()
         updateStepColors()
+        updateAccessibilityInfo()
+    }
+
+    private fun updateAccessibilityInfo() {
+        rootView.contentDescription = String.format(levelContentDescription, currentLevel, maxLevel)
+        rootView.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION)
     }
 
     private fun updateIconTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 30b87cc..f9e0b16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -18,7 +18,10 @@
 
 import android.content.Context
 import android.service.quicksettings.Tile
+import android.view.View
+import android.widget.Switch
 import com.android.systemui.common.shared.model.Icon
+import kotlin.reflect.KClass
 
 /**
  * Represents current a state of the tile to be displayed in on the view. Consider using
@@ -111,7 +114,7 @@
         var stateDescription: CharSequence? = null
         var sideViewIcon: SideViewIcon = SideViewIcon.None
         var enabledState: EnabledState = EnabledState.ENABLED
-        var expandedAccessibilityClassName: String? = null
+        var expandedAccessibilityClass: KClass<out View>? = Switch::class
 
         fun build(): QSTileState =
             QSTileState(
@@ -124,7 +127,7 @@
                 stateDescription,
                 sideViewIcon,
                 enabledState,
-                expandedAccessibilityClassName,
+                expandedAccessibilityClass?.qualifiedName,
             )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
index ffbc10a..2336a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.util.kotlin
 
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
 class Utils {
     companion object {
         fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
@@ -27,6 +30,45 @@
 
         fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
             Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth)
+
+        /**
+         * Samples the provided flows, emitting a tuple of the original flow's value as well as each
+         * of the combined flows' values.
+         *
+         * Flow<A>.sample(Flow<B>, Flow<C>) -> (A, B, C)
+         */
+        fun <A, B, C> Flow<A>.sample(b: Flow<B>, c: Flow<C>): Flow<Triple<A, B, C>> {
+            return this.sample(combine(b, c, ::Pair), ::toTriple)
+        }
+
+        /**
+         * Samples the provided flows, emitting a tuple of the original flow's value as well as each
+         * of the combined flows' values.
+         *
+         * Flow<A>.sample(Flow<B>, Flow<C>, Flow<D>) -> (A, B, C, D)
+         */
+        fun <A, B, C, D> Flow<A>.sample(
+            b: Flow<B>,
+            c: Flow<C>,
+            d: Flow<D>
+        ): Flow<Quad<A, B, C, D>> {
+            return this.sample(combine(b, c, d, ::Triple), ::toQuad)
+        }
+
+        /**
+         * Samples the provided flows, emitting a tuple of the original flow's value as well as each
+         * of the combined flows' values.
+         *
+         * Flow<A>.sample(Flow<B>, Flow<C>, Flow<D>, Flow<E>) -> (A, B, C, D, E)
+         */
+        fun <A, B, C, D, E> Flow<A>.sample(
+            b: Flow<B>,
+            c: Flow<C>,
+            d: Flow<D>,
+            e: Flow<E>,
+        ): Flow<Quint<A, B, C, D, E>> {
+            return this.sample(combine(b, c, d, e, ::Quad), ::toQuint)
+        }
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 6ac84bc..0c06808 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -100,12 +100,12 @@
         }
 
     @Test
-    fun isAutoConfirmEnabled() =
+    fun isAutoConfirmFeatureEnabled() =
         testScope.runTest {
             whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[0].id)).thenReturn(true)
             whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[1].id)).thenReturn(false)
 
-            val values by collectValues(underTest.isAutoConfirmEnabled)
+            val values by collectValues(underTest.isAutoConfirmFeatureEnabled)
             assertThat(values.first()).isFalse()
             assertThat(values.last()).isTrue()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 2f5f460..103f2b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -213,11 +213,14 @@
     @Test
     fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() =
         testScope.runTest {
+            val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
             val isThrottled by collectLastValue(underTest.isThrottled)
             utils.authenticationRepository.apply {
                 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
-                setAutoConfirmEnabled(true)
+                setAutoConfirmFeatureEnabled(true)
             }
+            assertThat(isAutoConfirmEnabled).isTrue()
+
             assertThat(
                     underTest.authenticate(
                         FakeAuthenticationRepository.DEFAULT_PIN.toMutableList().apply {
@@ -233,10 +236,13 @@
     @Test
     fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() =
         testScope.runTest {
+            val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
             utils.authenticationRepository.apply {
                 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
-                setAutoConfirmEnabled(true)
+                setAutoConfirmFeatureEnabled(true)
             }
+            assertThat(isAutoConfirmEnabled).isTrue()
+
             assertThat(
                     underTest.authenticate(
                         FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 },
@@ -251,10 +257,13 @@
     @Test
     fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() =
         testScope.runTest {
+            val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
             utils.authenticationRepository.apply {
                 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
-                setAutoConfirmEnabled(true)
+                setAutoConfirmFeatureEnabled(true)
             }
+            assertThat(isAutoConfirmEnabled).isTrue()
+
             assertThat(
                     underTest.authenticate(
                         FakeAuthenticationRepository.DEFAULT_PIN + listOf(7),
@@ -269,10 +278,13 @@
     @Test
     fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() =
         testScope.runTest {
+            val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
             utils.authenticationRepository.apply {
                 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
-                setAutoConfirmEnabled(true)
+                setAutoConfirmFeatureEnabled(true)
             }
+            assertThat(isAutoConfirmEnabled).isTrue()
+
             assertThat(
                     underTest.authenticate(
                         FakeAuthenticationRepository.DEFAULT_PIN,
@@ -285,11 +297,35 @@
         }
 
     @Test
+    fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringThrottling_returnsNullAndHasNoEffects() =
+        testScope.runTest {
+            val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
+            val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
+            val hintedPinLength by collectLastValue(underTest.hintedPinLength)
+            utils.authenticationRepository.apply {
+                setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
+                setAutoConfirmFeatureEnabled(true)
+                setThrottleDuration(42)
+            }
+
+            val authResult =
+                underTest.authenticate(
+                    FakeAuthenticationRepository.DEFAULT_PIN,
+                    tryAutoConfirm = true
+                )
+
+            assertThat(authResult).isEqualTo(AuthenticationResult.SKIPPED)
+            assertThat(isAutoConfirmEnabled).isFalse()
+            assertThat(isUnlocked).isFalse()
+            assertThat(hintedPinLength).isNull()
+        }
+
+    @Test
     fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() =
         testScope.runTest {
             utils.authenticationRepository.apply {
                 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
-                setAutoConfirmEnabled(false)
+                setAutoConfirmFeatureEnabled(false)
             }
             assertThat(
                     underTest.authenticate(
@@ -416,7 +452,7 @@
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
             utils.authenticationRepository.apply {
                 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
-                setAutoConfirmEnabled(false)
+                setAutoConfirmFeatureEnabled(false)
             }
 
             assertThat(hintedPinLength).isNull()
@@ -433,7 +469,7 @@
                         repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) }
                     }
                 )
-                setAutoConfirmEnabled(true)
+                setAutoConfirmFeatureEnabled(true)
             }
 
             assertThat(hintedPinLength).isNull()
@@ -445,7 +481,7 @@
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
             utils.authenticationRepository.apply {
                 setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
-                setAutoConfirmEnabled(true)
+                setAutoConfirmFeatureEnabled(true)
                 overrideCredential(
                     buildList {
                         repeat(utils.authenticationRepository.hintedPinLength) { add(it + 1) }
@@ -467,7 +503,7 @@
                         repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) }
                     }
                 )
-                setAutoConfirmEnabled(true)
+                setAutoConfirmFeatureEnabled(true)
             }
 
             assertThat(hintedPinLength).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 915661b..5775396 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -110,12 +110,14 @@
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
             val message by collectLastValue(underTest.message)
+            val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
 
             utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
             runCurrent()
-            utils.authenticationRepository.setAutoConfirmEnabled(true)
+            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
             utils.deviceEntryRepository.setUnlocked(false)
             underTest.showOrUnlockDevice()
+            assertThat(isAutoConfirmEnabled).isTrue()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
             assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
             underTest.clearMessage()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 6ef518e..6357a1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -226,6 +226,23 @@
             assertThat(isSideBySideSupported).isFalse()
         }
 
+    @Test
+    fun isFoldSplitRequired() =
+        testScope.runTest {
+            val isFoldSplitRequired by collectLastValue(underTest.isFoldSplitRequired)
+            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            assertThat(isFoldSplitRequired).isTrue()
+            utils.authenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Password
+            )
+            assertThat(isFoldSplitRequired).isFalse()
+
+            utils.authenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pattern
+            )
+            assertThat(isFoldSplitRequired).isTrue()
+        }
+
     private fun authMethodsToTest(): List<DomainLayerAuthenticationMethodModel> {
         return listOf(
             DomainLayerAuthenticationMethodModel.None,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 3ddac7e..e07c0b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -247,7 +247,7 @@
     fun onAutoConfirm_whenCorrect() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            utils.authenticationRepository.setAutoConfirmEnabled(true)
+            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
             lockDeviceAndOpenPinBouncer()
 
             FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
@@ -263,7 +263,7 @@
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
             val message by collectLastValue(bouncerViewModel.message)
             val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
-            utils.authenticationRepository.setAutoConfirmEnabled(true)
+            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
             lockDeviceAndOpenPinBouncer()
 
             FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit ->
@@ -323,7 +323,7 @@
         testScope.runTest {
             val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
             utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.authenticationRepository.setAutoConfirmEnabled(true)
+            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
 
             assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
         }
@@ -333,7 +333,7 @@
         testScope.runTest {
             val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
             utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.authenticationRepository.setAutoConfirmEnabled(true)
+            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
 
             underTest.onPinButtonClicked(1)
 
@@ -355,7 +355,7 @@
         testScope.runTest {
             val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance)
             utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.authenticationRepository.setAutoConfirmEnabled(true)
+            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
 
             assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index ca8316d..28fae81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -21,7 +21,6 @@
 import android.appwidget.AppWidgetProviderInfo
 import android.content.BroadcastReceiver
 import android.content.ComponentName
-import android.content.pm.PackageManager
 import android.os.UserHandle
 import android.os.UserManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -34,8 +33,6 @@
 import com.android.systemui.communal.shared.CommunalWidgetHost
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.res.R
@@ -72,16 +69,12 @@
 
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
 
-    @Mock private lateinit var packageManager: PackageManager
-
     @Mock private lateinit var userManager: UserManager
 
     @Mock private lateinit var userHandle: UserHandle
 
     @Mock private lateinit var userTracker: UserTracker
 
-    @Mock private lateinit var featureFlags: FeatureFlagsClassic
-
     @Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
 
     @Mock private lateinit var providerInfoA: AppWidgetProviderInfo
@@ -113,13 +106,13 @@
         communalRepository = FakeCommunalRepository()
 
         communalEnabled(true)
-        widgetOnKeyguardEnabled(true)
         setAppWidgetIds(emptyList())
 
         overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray())
 
         whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
         whenever(userTracker.userHandle).thenReturn(userHandle)
+        whenever(communalWidgetDao.getWidgets()).thenReturn(flowOf(emptyMap()))
     }
 
     @Test
@@ -213,7 +206,7 @@
         testScope.runTest {
             communalEnabled(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            collectLastValue(repository.communalWidgets)()
             verifyBroadcastReceiverNeverRegistered()
         }
 
@@ -222,7 +215,7 @@
         testScope.runTest {
             userUnlocked(true)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            collectLastValue(repository.communalWidgets)()
             verifyBroadcastReceiverNeverRegistered()
         }
 
@@ -231,7 +224,7 @@
         testScope.runTest {
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            collectLastValue(repository.communalWidgets)()
             verifyBroadcastReceiverRegistered()
         }
 
@@ -241,7 +234,7 @@
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
 
-            val job = launch { repository.stopwatchAppWidgetInfo.collect() }
+            val job = launch { repository.communalWidgets.collect() }
             runCurrent()
             val receiver = broadcastReceiverUpdate()
 
@@ -252,53 +245,16 @@
         }
 
     @Test
-    fun stopwatch_whenUserUnlocks_receiveProviderInfo() =
-        testScope.runTest {
-            userUnlocked(false)
-            val repository = initCommunalWidgetRepository()
-            val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
-            assertThat(lastStopwatchProviderInfo()).isNull()
-
-            userUnlocked(true)
-            installedProviders(listOf(stopwatchProviderInfo))
-            broadcastReceiverUpdate()
-
-            assertThat(lastStopwatchProviderInfo()?.providerInfo).isEqualTo(stopwatchProviderInfo)
-        }
-
-    @Test
-    fun stopwatch_userUnlockedButWidgetNotInstalled_noProviderInfo() =
-        testScope.runTest {
-            userUnlocked(true)
-            installedProviders(listOf())
-
-            val repository = initCommunalWidgetRepository()
-
-            val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
-            assertThat(lastStopwatchProviderInfo()).isNull()
-        }
-
-    @Test
-    fun appWidgetId_providerInfoAvailable_allocateAppWidgetId() =
-        testScope.runTest {
-            userUnlocked(true)
-            installedProviders(listOf(stopwatchProviderInfo))
-            val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
-            verify(appWidgetHost).allocateAppWidgetId()
-        }
-
-    @Test
     fun appWidgetHost_userUnlocked_startListening() =
         testScope.runTest {
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            collectLastValue(repository.communalWidgets)()
             verify(appWidgetHost, Mockito.never()).startListening()
 
             userUnlocked(true)
             broadcastReceiverUpdate()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            collectLastValue(repository.communalWidgets)()
 
             verify(appWidgetHost).startListening()
         }
@@ -308,18 +264,18 @@
         testScope.runTest {
             userUnlocked(false)
             val repository = initCommunalWidgetRepository()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            collectLastValue(repository.communalWidgets)()
 
             userUnlocked(true)
             broadcastReceiverUpdate()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            collectLastValue(repository.communalWidgets)()
 
             verify(appWidgetHost).startListening()
             verify(appWidgetHost, Mockito.never()).stopListening()
 
             userUnlocked(false)
             broadcastReceiverUpdate()
-            collectLastValue(repository.stopwatchAppWidgetInfo)()
+            collectLastValue(repository.communalWidgets)()
 
             verify(appWidgetHost).stopListening()
         }
@@ -334,11 +290,9 @@
             communalRepository,
             communalWidgetHost,
             communalWidgetDao,
-            packageManager,
             userManager,
             userTracker,
             logBuffer,
-            featureFlags,
         )
     }
 
@@ -385,10 +339,6 @@
         communalRepository.setIsCommunalEnabled(enabled)
     }
 
-    private fun widgetOnKeyguardEnabled(enabled: Boolean) {
-        whenever(featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)).thenReturn(enabled)
-    }
-
     private fun userUnlocked(userUnlocked: Boolean) {
         whenever(userManager.isUserUnlockingOrUnlocked(userHandle)).thenReturn(userUnlocked)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 08d54c0..a6c4f19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.coroutines.collectLastValue
@@ -45,7 +44,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
@@ -53,8 +51,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
 class CommunalInteractorTest : SysuiTestCase() {
-    @Mock private lateinit var stopwatchAppWidgetInfo: CommunalAppWidgetInfo
-
     private lateinit var testScope: TestScope
 
     private lateinit var tutorialRepository: FakeCommunalTutorialRepository
@@ -85,18 +81,6 @@
     }
 
     @Test
-    fun appWidgetInfoFlow() =
-        testScope.runTest {
-            val lastAppWidgetInfo = collectLastValue(underTest.appWidgetInfo)
-            runCurrent()
-            assertThat(lastAppWidgetInfo()).isNull()
-
-            widgetRepository.setStopwatchAppWidgetInfo(stopwatchAppWidgetInfo)
-            runCurrent()
-            assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo)
-        }
-
-    @Test
     fun communalEnabled() =
         testScope.runTest {
             communalRepository.setIsCommunalEnabled(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index 33a6667..a496292 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -7,7 +7,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
-import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -20,14 +19,13 @@
 @SmallTest
 class DefaultCommunalBlueprintTest : SysuiTestCase() {
     @Mock private lateinit var hubSection: DefaultCommunalHubSection
-    @Mock private lateinit var widgetSection: DefaultCommunalWidgetSection
 
     private lateinit var blueprint: DefaultCommunalBlueprint
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        blueprint = DefaultCommunalBlueprint(hubSection, widgetSection)
+        blueprint = DefaultCommunalBlueprint(hubSection)
     }
 
     @Test
@@ -35,7 +33,6 @@
         val constraintLayout = ConstraintLayout(context, null)
         blueprint.replaceViews(null, constraintLayout)
         verify(hubSection).addViews(constraintLayout)
-        verify(widgetSection).addViews(constraintLayout)
     }
 
     @Test
@@ -43,6 +40,5 @@
         val cs = ConstraintSet()
         blueprint.applyConstraints(cs)
         verify(hubSection).applyConstraints(cs)
-        verify(widgetSection).applyConstraints(cs)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index b5e7e2c..92c2d74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -130,7 +130,7 @@
                     "sd=null, " +
                     "svi=None, " +
                     "enabled=ENABLED, " +
-                    "a11y=null" +
+                    "a11y=android.widget.Switch" +
                     "], " +
                     "data=test_data"
             )
@@ -154,7 +154,7 @@
                     "sd=null, " +
                     "svi=None, " +
                     "enabled=ENABLED, " +
-                    "a11y=null], " +
+                    "a11y=android.widget.Switch], " +
                     "data=test_data"
             )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index bc28ccb..f4c05e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -376,6 +376,7 @@
         mFeatureFlags.set(Flags.MIGRATE_NSSL, false);
         mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
         mFeatureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false);
+        mFeatureFlags.set(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT, false);
         mMainDispatcher = getMainDispatcher();
         KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
                 KeyguardInteractorFactory.create();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index ddfe79a..81c5d9c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -41,8 +41,9 @@
     private val currentTime: () -> Long,
 ) : AuthenticationRepository {
 
-    private val _isAutoConfirmEnabled = MutableStateFlow(false)
-    override val isAutoConfirmEnabled: StateFlow<Boolean> = _isAutoConfirmEnabled.asStateFlow()
+    private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false)
+    override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
+        _isAutoConfirmFeatureEnabled.asStateFlow()
 
     override val hintedPinLength: Int = HINTING_PIN_LENGTH
 
@@ -98,8 +99,8 @@
         _throttling.value = throttlingModel
     }
 
-    fun setAutoConfirmEnabled(isEnabled: Boolean) {
-        _isAutoConfirmEnabled.value = isEnabled
+    fun setAutoConfirmFeatureEnabled(isEnabled: Boolean) {
+        _isAutoConfirmFeatureEnabled.value = isEnabled
     }
 
     override suspend fun setThrottleDuration(durationMs: Int) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 8a2ff50..c6f12e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,22 +1,14 @@
 package com.android.systemui.communal.data.repository
 
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 
 /** Fake implementation of [CommunalWidgetRepository] */
 class FakeCommunalWidgetRepository : CommunalWidgetRepository {
-    private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
-    override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
-
     private val _communalWidgets = MutableStateFlow<List<CommunalWidgetContentModel>>(emptyList())
     override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = _communalWidgets
 
-    fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
-        _stopwatchAppWidgetInfo.value = appWidgetInfo
-    }
-
     fun setCommunalWidgets(inventory: List<CommunalWidgetContentModel>) {
         _communalWidgets.value = inventory
     }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 052b0c2..e5f7637 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2391,7 +2391,8 @@
         long token = Binder.clearCallingIdentity();
         try {
             // Permissions are managed by UIDs, but unfortunately a package name is required in API.
-            String packageName = ArrayUtils.firstOrNull(packageManager.getPackagesForUid(uid));
+            String packageName = ArrayUtils.firstOrNull(ArrayUtils.defeatNullable(
+                    packageManager.getPackagesForUid(uid)));
             if (packageName == null) {
                 return false;
             }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1ae67dd..8171e71 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -20,6 +20,7 @@
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
 import static android.provider.Settings.Secure.STYLUS_HANDWRITING_DEFAULT_VALUE;
 import static android.provider.Settings.Secure.STYLUS_HANDWRITING_ENABLED;
 import static android.server.inputmethod.InputMethodManagerServiceProto.BACK_DISPOSITION;
@@ -6459,6 +6460,11 @@
                     if (!userHasDebugPriv(userId, shellCommand)) {
                         continue;
                     }
+                    // Skip on headless user
+                    if (USER_TYPE_SYSTEM_HEADLESS.equals(
+                            mUserManagerInternal.getUserInfo(userId).userType)) {
+                        continue;
+                    }
                     final String nextIme;
                     final List<InputMethodInfo> nextEnabledImes;
                     if (userId == mSettings.getCurrentUserId()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 86e417b..3b5ef4c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -77,13 +77,14 @@
     void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
         if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
 
-        final boolean isScreenLocked = isScreenLocked();
-
-        final String lastInputMethodId = mSettings.getSelectedInputMethod();
-        int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
-        if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
-
         synchronized (ImfLock.class) {
+            final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
+                    && mWindowManagerInternal.isKeyguardSecure(mSettings.getCurrentUserId());
+            final String lastInputMethodId = mSettings.getSelectedInputMethod();
+            int lastInputMethodSubtypeId =
+                    mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
+            if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
+
             final List<ImeSubtypeListItem> imList = mSwitchingController
                     .getSortedInputMethodAndSubtypeListForImeMenuLocked(
                             showAuxSubtypes, isScreenLocked);
@@ -200,12 +201,8 @@
             mService.updateSystemUiLocked();
             mService.sendOnNavButtonFlagsChangedLocked();
             mSwitchingDialog.show();
-        }
-    }
 
-    private boolean isScreenLocked() {
-        return mWindowManagerInternal.isKeyguardLocked()
-                && mWindowManagerInternal.isKeyguardSecure(mSettings.getCurrentUserId());
+        }
     }
 
     void updateKeyboardFromSettingsLocked() {
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index cba605e..2617703 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -60,6 +60,7 @@
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatureArrays;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
 import static com.android.server.pm.PackageManagerServiceUtils.isSystemOrRootOrShell;
+import static com.android.server.pm.parsing.PackageInfoUtils.getDeprecatedSignatures;
 import static com.android.server.pm.resolution.ComponentResolver.RESOLVE_PRIORITY_SORTER;
 
 import android.Manifest;
@@ -1531,6 +1532,7 @@
             pi.applicationInfo = PackageInfoUtils.generateDelegateApplicationInfo(
                     ai, flags, state, userId);
             pi.signingInfo = ps.getSigningInfo();
+            pi.signatures = getDeprecatedSignatures(pi.signingInfo.getSigningDetails(), flags);
 
             if (DEBUG_PACKAGE_INFO) {
                 Log.v(TAG, "ps.pkg is n/a for ["
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index c26cf1c..38cde3e 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -238,21 +238,7 @@
         }
 
         final SigningDetails signingDetails = pkg.getSigningDetails();
-        // deprecated method of getting signing certificates
-        if ((flags & PackageManager.GET_SIGNATURES) != 0) {
-            if (signingDetails.hasPastSigningCertificates()) {
-                // Package has included signing certificate rotation information.  Return the oldest
-                // cert so that programmatic checks keep working even if unaware of key rotation.
-                info.signatures = new Signature[1];
-                info.signatures[0] = signingDetails.getPastSigningCertificates()[0];
-            } else if (signingDetails.hasSignatures()) {
-                // otherwise keep old behavior
-                int numberOfSigs = signingDetails.getSignatures().length;
-                info.signatures = new Signature[numberOfSigs];
-                System.arraycopy(signingDetails.getSignatures(), 0, info.signatures, 0,
-                        numberOfSigs);
-            }
-        }
+        info.signatures = getDeprecatedSignatures(signingDetails, flags);
 
         // replacement for GET_SIGNATURES
         if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
@@ -358,6 +344,30 @@
         return info;
     }
 
+    /**
+     *  Retrieve the deprecated {@link PackageInfo.signatures} field of signing certificates
+     */
+    public static Signature[] getDeprecatedSignatures(SigningDetails signingDetails, long flags) {
+        if ((flags & PackageManager.GET_SIGNATURES) == 0) {
+            return null;
+        }
+        if (signingDetails.hasPastSigningCertificates()) {
+            // Package has included signing certificate rotation information.  Return the oldest
+            // cert so that programmatic checks keep working even if unaware of key rotation.
+            Signature[] signatures = new Signature[1];
+            signatures[0] = signingDetails.getPastSigningCertificates()[0];
+            return signatures;
+        } else if (signingDetails.hasSignatures()) {
+            // otherwise keep old behavior
+            int numberOfSigs = signingDetails.getSignatures().length;
+            Signature[] signatures = new Signature[numberOfSigs];
+            System.arraycopy(signingDetails.getSignatures(), 0, signatures, 0,
+                    numberOfSigs);
+            return signatures;
+        }
+        return null;
+    }
+
     private static void updateApplicationInfo(ApplicationInfo ai, long flags,
             PackageUserState state) {
         if ((flags & PackageManager.GET_META_DATA) == 0) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 1b5631f5..a2f5a38 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -256,13 +256,24 @@
             mOriginatingPendingIntent = originatingPendingIntent;
             mIntent = intent;
             mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
+            if (originatingPendingIntent == null) {
+                // grant creator BAL privileges unless explicitly opted out
+                mBalAllowedByPiCreator =
+                        checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+                                ? BackgroundStartPrivileges.NONE
+                                : BackgroundStartPrivileges.ALLOW_BAL;
+            } else {
+                // for PendingIntents we restrict creator BAL based on target_sdk
+                mBalAllowedByPiCreator =
+                        checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+                                ? BackgroundStartPrivileges.NONE
+                                : BackgroundStartPrivileges.ALLOW_BAL;
+            }
             mBalAllowedByPiSender =
-                    PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(checkedOptions,
-                            realCallingUid, mRealCallingPackage);
-            mBalAllowedByPiCreator =
-                    checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
-                            == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
-                            ? BackgroundStartPrivileges.NONE : BackgroundStartPrivileges.ALLOW_BAL;
+                    PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+                            checkedOptions, realCallingUid, mRealCallingPackage);
             mAppSwitchState = mService.getBalAppSwitchesState();
             mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
             mIsCallingUidPersistentSystemProcess =
@@ -306,10 +317,14 @@
             return name + "[debugOnly]";
         }
 
-        private boolean isPendingIntent() {
+        private boolean hasRealCaller() {
             return mRealCallingUid != NO_PROCESS_UID;
         }
 
+        private boolean isPendingIntent() {
+            return mOriginatingPendingIntent != null;
+        }
+
         private String dump(BalVerdict resultIfPiCreatorAllowsBal) {
             Preconditions.checkState(!isPendingIntent());
             return dump(resultIfPiCreatorAllowsBal, null);
@@ -334,7 +349,9 @@
             sb.append("; isCallingUidPersistentSystemProcess: ")
                     .append(mIsCallingUidPersistentSystemProcess);
             sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
-            if (isPendingIntent()) {
+            sb.append("; hasRealCaller: ").append(hasRealCaller());
+            sb.append("; isPendingIntent: ").append(isPendingIntent());
+            if (hasRealCaller()) {
                 sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
                 sb.append("; realCallingPackage: ")
                         .append(getDebugPackageName(mRealCallingPackage, mRealCallingUid));
@@ -351,13 +368,13 @@
             sb.append("; mForcedBalByPiSender: ").append(mForcedBalByPiSender);
             sb.append("; intent: ").append(mIntent);
             sb.append("; callerApp: ").append(mCallerApp);
-            if (isPendingIntent()) {
+            if (hasRealCaller()) {
                 sb.append("; realCallerApp: ").append(mRealCallerApp);
             }
             if (mCallerApp != null) {
                 sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
             }
-            if (isPendingIntent()) {
+            if (hasRealCaller()) {
                 if (mRealCallerApp != null) {
                     sb.append("; realInVisibleTask: ")
                             .append(mRealCallerApp.hasActivityInVisibleTask());
@@ -484,7 +501,7 @@
 
         BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
 
-        if (!state.isPendingIntent()) {
+        if (!state.hasRealCaller()) {
             if (resultForCaller.allows()) {
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Background activity start allowed. "
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index 80427b3..505421e 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -494,12 +494,21 @@
 ::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::setCallback(
         const std::shared_ptr<TvInputCallbackWrapper>& in_callback) {
     if (mIsHidl) {
-        in_callback->aidlTvInputCallback = nullptr;
-        return hidlSetCallback(in_callback == nullptr ? nullptr : in_callback->hidlTvInputCallback);
+        if (in_callback == nullptr) {
+            return hidlSetCallback(nullptr);
+        }
+        else {
+            in_callback->aidlTvInputCallback = nullptr;
+            return hidlSetCallback(in_callback->hidlTvInputCallback);
+        }
     } else {
-        in_callback->hidlTvInputCallback = nullptr;
-        return mAidlTvInput->setCallback(in_callback == nullptr ? nullptr
-                                                                : in_callback->aidlTvInputCallback);
+        if (in_callback == nullptr) {
+            return mAidlTvInput->setCallback(nullptr);
+        }
+        else {
+            in_callback->hidlTvInputCallback = nullptr;
+            return mAidlTvInput->setCallback(in_callback->aidlTvInputCallback);
+        }
     }
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 34d6755..9b62a2c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9667,6 +9667,26 @@
         }
     }
 
+    @Override
+    public ComponentName getDeviceOwnerComponentOnUser(int userId) {
+        if (!mHasFeature) {
+            return null;
+        }
+        if (mInjector.userHandleGetCallingUserId() != userId) {
+            Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+                    || hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+        }
+        synchronized (getLockObject()) {
+            // There is only ever one device owner on a device so if the passed userId is the same
+            // as the device owner userId we know that the componentName returned by
+            // getDeviceOwnerComponent will be the correct one.
+            if (mOwners.getDeviceOwnerUserId() == userId || userId == UserHandle.USER_ALL) {
+                return mOwners.getDeviceOwnerComponent();
+            }
+        }
+        return null;
+    }
+
     private int getDeviceOwnerUserIdUncheckedLocked() {
         return mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerUserId() : UserHandle.USER_NULL;
     }
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index 15a5859..7db09f9 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -16,7 +16,9 @@
 
 package com.android.server.permission.access.permission
 
+import android.Manifest
 import android.permission.PermissionManager
+import android.permission.flags.Flags
 import android.util.Slog
 import com.android.modules.utils.BinaryXmlPullParser
 import com.android.modules.utils.BinaryXmlSerializer
@@ -273,7 +275,12 @@
 
         /** These permissions are supported for virtual devices. */
         // TODO: b/298661870 - Use new API to get the list of device aware permissions.
-        val DEVICE_AWARE_PERMISSIONS = emptySet<String>()
+        val DEVICE_AWARE_PERMISSIONS =
+            if (Flags.deviceAwarePermissionApis()) {
+                setOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
+            } else {
+                emptySet<String>()
+            }
     }
 
     /**
diff --git a/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl
new file mode 100644
index 0000000..4c37a6d
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.telephony.satellite.SatelliteCapabilities;
+
+/**
+ * Interface for satellite capabilities change callback.
+ * @hide
+ */
+oneway interface ISatelliteCapabilitiesCallback {
+    /**
+     * Called when satellite capability has changed.
+     *
+     * @param capabilities The new satellite capability.
+     */
+    void onSatelliteCapabilitiesChanged(in SatelliteCapabilities capabilities);
+}
+
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java b/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java
new file mode 100644
index 0000000..b68dd5a
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for satellite capabilities change events.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteCapabilitiesCallback {
+    /**
+     * Called when satellite capability has changed.
+     * @param capabilities The new satellite capabilities.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSatelliteCapabilitiesChanged(@NonNull SatelliteCapabilities capabilities);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index f18cbea..71786b3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -81,6 +81,9 @@
             new ConcurrentHashMap<>();
     private static final ConcurrentHashMap<NtnSignalStrengthCallback, INtnSignalStrengthCallback>
             sNtnSignalStrengthCallbackMap = new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<SatelliteCapabilitiesCallback,
+            ISatelliteCapabilitiesCallback>
+            sSatelliteCapabilitiesCallbackMap = new ConcurrentHashMap<>();
 
     private final int mSubId;
 
@@ -2018,7 +2021,7 @@
      * {@link TelephonyManager#unregisterTelephonyCallback(TelephonyCallback)}..
      * </p>
      *
-     * @param callback The callback that was passed to
+     * @param callback The callback that was passed to.
      * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
      *
      * @throws SecurityException if the caller doesn't have required permission.
@@ -2046,9 +2049,84 @@
             loge("unregisterForNtnSignalStrengthChanged() RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-
     }
 
+    /**
+     * Registers for satellite capabilities change event from the satellite service.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle the satellite capabilities changed event.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @SatelliteResult public int registerForSatelliteCapabilitiesChanged(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteCapabilitiesCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ISatelliteCapabilitiesCallback internalCallback =
+                        new ISatelliteCapabilitiesCallback.Stub() {
+                            @Override
+                            public void onSatelliteCapabilitiesChanged(
+                                    SatelliteCapabilities capabilities) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSatelliteCapabilitiesChanged(
+                                                capabilities)));
+                            }
+                        };
+                sSatelliteCapabilitiesCallbackMap.put(callback, internalCallback);
+                return telephony.registerForSatelliteCapabilitiesChanged(mSubId, internalCallback);
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForSatelliteCapabilitiesChanged() RemoteException: " + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return SATELLITE_RESULT_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregisters for satellite capabilities change event from the satellite service.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to.
+     * {@link #registerForSatelliteCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void unregisterForSatelliteCapabilitiesChanged(
+            @NonNull SatelliteCapabilitiesCallback callback) {
+        Objects.requireNonNull(callback);
+        ISatelliteCapabilitiesCallback internalCallback =
+                sSatelliteCapabilitiesCallbackMap.remove(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    telephony.unregisterForSatelliteCapabilitiesChanged(mSubId, internalCallback);
+                } else {
+                    loge("unregisterForSatelliteCapabilitiesChanged: No internal callback.");
+                }
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForSatelliteCapabilitiesChanged() RemoteException: " + ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
 
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
index d44ddfa..ccca5ad 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
@@ -19,6 +19,7 @@
 import android.telephony.satellite.stub.NtnSignalStrength;
 import android.telephony.satellite.stub.NTRadioTechnology;
 import android.telephony.satellite.stub.PointingInfo;
+import android.telephony.satellite.stub.SatelliteCapabilities;
 import android.telephony.satellite.stub.SatelliteDatagram;
 import android.telephony.satellite.stub.SatelliteModemState;
 
@@ -62,7 +63,15 @@
 
     /**
      * Called when NTN signal strength changes.
+     *
      * @param ntnSignalStrength The new NTN signal strength.
      */
     void onNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
+
+    /**
+     * Called when satellite capabilities of the satellite service have changed.
+     *
+     * @param SatelliteCapabilities The current satellite capabilities.
+     */
+    void onSatelliteCapabilitiesChanged(in SatelliteCapabilities capabilities);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c212e35..15a20cb 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -68,6 +68,7 @@
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.aidl.IRcsConfigCallback;
 import android.telephony.satellite.INtnSignalStrengthCallback;
+import android.telephony.satellite.ISatelliteCapabilitiesCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
@@ -3123,4 +3124,27 @@
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
     void unregisterForNtnSignalStrengthChanged(int subId,
             in INtnSignalStrengthCallback callback);
+
+    /**
+     * Registers for satellite capabilities change event from the satellite service.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle the satellite capabilities changed event.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    int registerForSatelliteCapabilitiesChanged(int subId,
+            in ISatelliteCapabilitiesCallback callback);
+
+    /**
+     * Unregisters for satellite capabilities change event from the satellite service.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to.
+     * {@link #registerForSatelliteCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void unregisterForSatelliteCapabilitiesChanged(int subId,
+            in ISatelliteCapabilitiesCallback callback);
 }