Merge "Update tests and canvas subclasses to expect drawImageRect calls" into main
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index c65e506..de6f023 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -12,4 +12,11 @@
     namespace: "backstage_power"
     description: "Throw an exception if an unsupported app uses JobInfo.setBias"
     bug: "300477393"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "batch_jobs_on_network_activation"
+    namespace: "backstage_power"
+    description: "Have JobScheduler attempt to delay the start of some connectivity jobs until the network is actually active"
+    bug: "318394184"
+}
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 45515dd..c1c5c0e 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -142,10 +142,7 @@
     private static final String KEY_ACCOUNT =
             "android.accounts.AbstractAccountAuthenticator.KEY_ACCOUNT";
 
-    private final Context mContext;
-
     public AbstractAccountAuthenticator(Context context) {
-        mContext = context;
     }
 
     private class Transport extends IAccountAuthenticator.Stub {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4b24b1f..1db1caf 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -71,6 +71,7 @@
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.LongSparseLongArray;
 import android.util.Pools;
@@ -7705,6 +7706,14 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(int code, int uid, @Mode int mode) {
         try {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (code == OP_BLUETOOTH_CONNECT) {
+                Log.i(DEBUG_LOGGING_TAG,
+                        "setUidMode called for OP_BLUETOOTH_CONNECT with mode: " + mode
+                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
+                                + " trace: "
+                                + Arrays.toString(Thread.currentThread().getStackTrace()));
+            }
             mService.setUidMode(code, uid, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -7725,6 +7734,15 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
         try {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (appOp.equals(OPSTR_BLUETOOTH_CONNECT)) {
+                Log.i(DEBUG_LOGGING_TAG,
+                        "setUidMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
+                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
+                                + " trace: "
+                                + Arrays.toString(Thread.currentThread().getStackTrace()));
+            }
+
             mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -7765,6 +7783,14 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setMode(int code, int uid, String packageName, @Mode int mode) {
         try {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (code == OP_BLUETOOTH_CONNECT) {
+                Log.i(DEBUG_LOGGING_TAG,
+                        "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
+                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
+                                + " trace: "
+                                + Arrays.toString(Thread.currentThread().getStackTrace()));
+            }
             mService.setMode(code, uid, packageName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -7787,6 +7813,14 @@
     public void setMode(@NonNull String op, int uid, @Nullable String packageName,
             @Mode int mode) {
         try {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (op.equals(OPSTR_BLUETOOTH_CONNECT)) {
+                Log.i(DEBUG_LOGGING_TAG,
+                        "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
+                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
+                                + " trace: "
+                                + Arrays.toString(Thread.currentThread().getStackTrace()));
+            }
             mService.setMode(strOpToOp(op), uid, packageName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index ad3ccc4..3fcb3da 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -37,6 +37,7 @@
 import android.os.RemoteException;
 import android.provider.DeviceConfig;
 import android.util.Log;
+import android.view.autofill.IAutoFillManagerClient;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -135,7 +136,8 @@
             @Nullable CancellationSignal cancellationSignal,
             @CallbackExecutor @NonNull Executor executor,
             @NonNull OutcomeReceiver<GetCandidateCredentialsResponse,
-                    GetCandidateCredentialsException> callback
+                    GetCandidateCredentialsException> callback,
+            @NonNull IAutoFillManagerClient clientCallback
     ) {
         requireNonNull(request, "request must not be null");
         requireNonNull(callingPackage, "callingPackage must not be null");
@@ -153,6 +155,7 @@
                     mService.getCandidateCredentials(
                             request,
                             new GetCandidateCredentialsTransport(executor, callback),
+                            clientCallback,
                             callingPackage);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
index 1b130a9..530fead 100644
--- a/core/java/android/credentials/GetCandidateCredentialsResponse.java
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Hide;
 import android.annotation.NonNull;
+import android.app.PendingIntent;
 import android.credentials.ui.GetCredentialProviderData;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -35,22 +36,39 @@
  */
 @Hide
 public final class GetCandidateCredentialsResponse implements Parcelable {
-    // TODO(b/299321990): Add members
-
     @NonNull
     private final List<GetCredentialProviderData> mCandidateProviderDataList;
 
+    private final PendingIntent mPendingIntent;
+
+    private final GetCredentialResponse mGetCredentialResponse;
+
     /**
      * @hide
      */
     @Hide
     public GetCandidateCredentialsResponse(
-            List<GetCredentialProviderData> candidateProviderDataList
+            GetCredentialResponse getCredentialResponse
+    ) {
+        mCandidateProviderDataList = null;
+        mPendingIntent = null;
+        mGetCredentialResponse = getCredentialResponse;
+    }
+
+    /**
+     * @hide
+     */
+    @Hide
+    public GetCandidateCredentialsResponse(
+            List<GetCredentialProviderData> candidateProviderDataList,
+            PendingIntent pendingIntent
     ) {
         Preconditions.checkCollectionNotEmpty(
                 candidateProviderDataList,
                 /*valueName=*/ "candidateProviderDataList");
         mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
+        mPendingIntent = pendingIntent;
+        mGetCredentialResponse = null;
     }
 
     /**
@@ -62,17 +80,40 @@
         return mCandidateProviderDataList;
     }
 
+    /**
+     * Returns candidate provider data list.
+     *
+     * @hide
+     */
+    public GetCredentialResponse getGetCredentialResponse() {
+        return mGetCredentialResponse;
+    }
+
+    /**
+     * Returns candidate provider data list.
+     *
+     * @hide
+     */
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
     protected GetCandidateCredentialsResponse(Parcel in) {
         List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
         in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
         mCandidateProviderDataList = candidateProviderDataList;
 
         AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);
+
+        mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+        mGetCredentialResponse = in.readTypedObject(GetCredentialResponse.CREATOR);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeTypedList(mCandidateProviderDataList);
+        dest.writeTypedObject(mPendingIntent, flags);
+        dest.writeTypedObject(mGetCredentialResponse, flags);
     }
 
     @Override
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index d081576..726bc97 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -22,6 +22,7 @@
 import android.credentials.ClearCredentialStateRequest;
 import android.credentials.CreateCredentialRequest;
 import android.credentials.GetCandidateCredentialsRequest;
+import android.view.autofill.IAutoFillManagerClient;
 import android.credentials.GetCredentialRequest;
 import android.credentials.RegisterCredentialDescriptionRequest;
 import android.credentials.UnregisterCredentialDescriptionRequest;
@@ -47,7 +48,7 @@
 
     @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
 
-    @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, String callingPackage);
+    @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, in IAutoFillManagerClient clientCallback, String callingPackage);
 
     @nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
 
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index e714887..f5b3a7b 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -114,10 +114,16 @@
     /** Format: 8 bits red */
     @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
     public static final int R_8           = 0x38;
-    /** Format: 16 bits red */
+    /**
+     * Format: 16 bits red. Bits should be represented in unsigned integer, instead of the
+     * implicit unsigned normalized.
+     */
     @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
     public static final int R_16_UINT     = 0x39;
-    /** Format: 16 bits each red, green */
+    /**
+     * Format: 16 bits each red, green. Bits should be represented in unsigned integer,
+     * instead of the implicit unsigned normalized.
+     */
     @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
     public static final int RG_1616_UINT  = 0x3a;
     /** Format: 10 bits each red, green, blue, alpha */
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index cb1b5d3..5ad2502 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -37,6 +37,7 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
+import android.view.autofill.IAutoFillManagerClient;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -621,6 +622,23 @@
                     new FillCallback(callback, request.getId())));
         }
 
+
+        @Override
+        public void onFillCredentialRequest(FillRequest request, IFillCallback callback,
+                IAutoFillManagerClient autofillClientCallback) {
+            ICancellationSignal transport = CancellationSignal.createTransport();
+            try {
+                callback.onCancellable(transport);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onFillCredentialRequest,
+                    AutofillService.this, request, CancellationSignal.fromTransport(transport),
+                    new FillCallback(callback, request.getId()),
+                    autofillClientCallback));
+        }
+
         @Override
         public void onSaveRequest(SaveRequest request, ISaveCallback callback) {
             mHandler.sendMessage(obtainMessage(
@@ -683,6 +701,15 @@
             @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
 
     /**
+     * Variant of onFillRequest for internal credential manager proxy autofill service only.
+     *
+     * @hide
+     */
+    public void onFillCredentialRequest(@NonNull FillRequest request,
+            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback,
+            IAutoFillManagerClient autofillClientCallback) {}
+
+    /**
      * Called when the user requests the service to save the contents of a screen.
      *
      * <p>If the service could not handle the request right away&mdash;for example, because it must
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index d88e094..03ead32 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -20,6 +20,7 @@
 import android.service.autofill.IFillCallback;
 import android.service.autofill.ISaveCallback;
 import android.service.autofill.SaveRequest;
+import android.view.autofill.IAutoFillManagerClient;
 import com.android.internal.os.IResultReceiver;
 
 /**
@@ -30,6 +31,8 @@
 oneway interface IAutoFillService {
     void onConnectedStateChanged(boolean connected);
     void onFillRequest(in FillRequest request, in IFillCallback callback);
+    void onFillCredentialRequest(in FillRequest request, in IFillCallback callback,
+    in IAutoFillManagerClient client);
     void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
     void onSavedPasswordCountRequest(in IResultReceiver receiver);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index dc82fc1..88949b2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -55,6 +55,26 @@
             "persist.wm.debug.desktop_stashing", false);
 
     /**
+     * Flag to indicate whether to apply shadows to windows in desktop mode.
+     */
+    private static final boolean USE_WINDOW_SHADOWS = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_use_window_shadows", true);
+
+    /**
+     * Flag to indicate whether to apply shadows to the focused window in desktop mode.
+     *
+     * Note: this flag is only relevant if USE_WINDOW_SHADOWS is false.
+     */
+    private static final boolean USE_WINDOW_SHADOWS_FOCUSED_WINDOW = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_use_window_shadows_focused_window", false);
+
+    /**
+     * Flag to indicate whether to apply shadows to windows in desktop mode.
+     */
+    private static final boolean USE_ROUNDED_CORNERS = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_use_rounded_corners", true);
+
+    /**
      * Return {@code true} is desktop windowing proto 2 is enabled
      */
     public static boolean isEnabled() {
@@ -81,4 +101,21 @@
     public static boolean isStashingEnabled() {
         return IS_STASHING_ENABLED;
     }
+
+    /**
+     * Return whether to use window shadows.
+     *
+     * @param isFocusedWindow whether the window to apply shadows to is focused
+     */
+    public static boolean useWindowShadow(boolean isFocusedWindow) {
+        return USE_WINDOW_SHADOWS
+            || (USE_WINDOW_SHADOWS_FOCUSED_WINDOW && isFocusedWindow);
+    }
+
+    /**
+     * Return whether to use rounded corners for windows.
+     */
+    public static boolean useRoundedCorners() {
+        return USE_ROUNDED_CORNERS;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 144555d..4a1bcaa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -721,6 +721,9 @@
             finishTransaction: SurfaceControl.Transaction
     ) {
         // Add rounded corners to freeform windows
+        if (!DesktopModeStatus.useRoundedCorners()) {
+            return
+        }
         val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
         info.changes
                 .filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 3b6be8a..2f51278 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -46,6 +46,7 @@
 import android.widget.ImageButton;
 import android.window.WindowContainerTransaction;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.IconProvider;
@@ -195,46 +196,16 @@
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             boolean applyStartTransactionOnDraw) {
-        final int shadowRadiusID = taskInfo.isFocused
-                ? R.dimen.freeform_decor_shadow_focused_thickness
-                : R.dimen.freeform_decor_shadow_unfocused_thickness;
-        final boolean isFreeform =
-                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
-        final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
-
         if (isHandleMenuActive()) {
             mHandleMenu.relayout(startT);
         }
 
+        updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw);
+
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
-        final int windowDecorLayoutId = getDesktopModeWindowDecorLayoutId(
-                taskInfo.getWindowingMode());
-        mRelayoutParams.reset();
-        mRelayoutParams.mRunningTaskInfo = taskInfo;
-        mRelayoutParams.mLayoutResId = windowDecorLayoutId;
-        mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
-        mRelayoutParams.mShadowRadiusId = shadowRadiusID;
-        mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
-        // The configuration used to lay out the window decoration. The system context's config is
-        // used when the task density has been overridden to a custom density so that the resources
-        // and views of the decoration aren't affected and match the rest of the System UI, if not
-        // then just use the task's configuration. A copy is made instead of using the original
-        // reference so that the configuration isn't mutated on config changes and diff checks can
-        // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
-        // See b/301119301.
-        // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
-        // instead of using a whole Configuration as a parameter.
-        final Configuration windowDecorConfig = new Configuration();
-        windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
-                ? mContext.getResources().getConfiguration() // Use system context.
-                : mTaskInfo.configuration); // Use task configuration.
-        mRelayoutParams.mWindowDecorConfig = windowDecorConfig;
-
-        mRelayoutParams.mCornerRadius =
-                (int) ScreenDecorationsUtils.getWindowCornerRadius(mContext);
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
 
@@ -273,6 +244,9 @@
             closeMaximizeMenu();
         }
 
+        final boolean isFreeform =
+                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+        final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
         if (!isDragResizeable) {
             if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
                 // We still want to track caption bar's exclusion region on a non-resizeable task.
@@ -323,6 +297,45 @@
         }
     }
 
+    @VisibleForTesting
+    static void updateRelayoutParams(
+            RelayoutParams relayoutParams,
+            Context context,
+            ActivityManager.RunningTaskInfo taskInfo,
+            boolean applyStartTransactionOnDraw) {
+        relayoutParams.reset();
+        relayoutParams.mRunningTaskInfo = taskInfo;
+        relayoutParams.mLayoutResId =
+            getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
+        relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
+        if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) {
+            relayoutParams.mShadowRadiusId = taskInfo.isFocused
+                    ? R.dimen.freeform_decor_shadow_focused_thickness
+                    : R.dimen.freeform_decor_shadow_unfocused_thickness;
+        }
+        relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
+        // The configuration used to lay out the window decoration. The system context's config is
+        // used when the task density has been overridden to a custom density so that the resources
+        // and views of the decoration aren't affected and match the rest of the System UI, if not
+        // then just use the task's configuration. A copy is made instead of using the original
+        // reference so that the configuration isn't mutated on config changes and diff checks can
+        // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
+        // See b/301119301.
+        // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
+        // instead of using a whole Configuration as a parameter.
+        final Configuration windowDecorConfig = new Configuration();
+        windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
+                ? context.getResources().getConfiguration() // Use system context.
+                : taskInfo.configuration); // Use task configuration.
+        relayoutParams.mWindowDecorConfig = windowDecorConfig;
+
+        if (DesktopModeStatus.useRoundedCorners()) {
+            relayoutParams.mCornerRadius =
+                    (int) ScreenDecorationsUtils.getWindowCornerRadius(context);
+        }
+    }
+
+
     private PointF calculateMaximizeMenuPosition() {
         final PointF position = new PointF();
         final Resources resources = mContext.getResources();
@@ -684,7 +697,7 @@
         super.close();
     }
 
-    private int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
+    private static int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
         return windowingMode == WINDOWING_MODE_FREEFORM
                 ? R.layout.desktop_mode_app_controls_window_decor
                 : R.layout.desktop_mode_focused_window_decor;
@@ -730,6 +743,10 @@
 
     @Override
     int getCaptionHeightId(@WindowingMode int windowingMode) {
+        return getCaptionHeightIdStatic(windowingMode);
+    }
+
+    private static int getCaptionHeightIdStatic(@WindowingMode int windowingMode) {
         return windowingMode == WINDOWING_MODE_FULLSCREEN
                 ? R.dimen.desktop_mode_fullscreen_decor_caption_height
                 : R.dimen.desktop_mode_freeform_decor_caption_height;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 18fcdd0..77667ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -16,16 +16,24 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.os.Handler;
+import android.os.SystemProperties;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.SurfaceControl;
@@ -34,14 +42,17 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
 
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -57,6 +68,13 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class DesktopModeWindowDecorationTests extends ShellTestCase {
+    private static final String USE_WINDOW_SHADOWS_SYSPROP_KEY =
+            "persist.wm.debug.desktop_use_window_shadows";
+    private static final String FOCUSED_USE_WINDOW_SHADOWS_SYSPROP_KEY =
+            "persist.wm.debug.desktop_use_window_shadows_focused_window";
+    private static final String USE_ROUNDED_CORNERS_SYSPROP_KEY =
+            "persist.wm.debug.desktop_use_rounded_corners";
+
     @Mock
     private DisplayController mMockDisplayController;
     @Mock
@@ -79,14 +97,29 @@
     private SurfaceControlViewHost mMockSurfaceControlViewHost;
     @Mock
     private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
+    @Mock
+    private TypedArray mMockRoundedCornersRadiusArray;
 
     private final Configuration mConfiguration = new Configuration();
 
+    private TestableContext mTestableContext;
+
+    /** Set up run before test class. */
+    @BeforeClass
+    public static void setUpClass() {
+        // Reset the sysprop settings before running the test.
+        SystemProperties.set(USE_WINDOW_SHADOWS_SYSPROP_KEY, "");
+        SystemProperties.set(FOCUSED_USE_WINDOW_SHADOWS_SYSPROP_KEY, "");
+        SystemProperties.set(USE_ROUNDED_CORNERS_SYSPROP_KEY, "");
+    }
+
     @Before
     public void setUp() {
         doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
                 any(), any(), any());
         doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
+        mTestableContext = new TestableContext(mContext);
+        mTestableContext.ensureTestableResources();
     }
 
     @Test
@@ -105,6 +138,50 @@
 
     }
 
+    @Test
+    public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreEnabled() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true);
+
+        assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
+    }
+
+    @Test
+    public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersAreEnabled() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        fillRoundedCornersResources(/* fillValue= */ 30);
+        RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true);
+
+        assertThat(relayoutParams.mCornerRadius).isGreaterThan(0);
+    }
+
+    private void fillRoundedCornersResources(int fillValue) {
+        when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt()))
+                .thenReturn(fillValue);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.array.config_roundedCornerRadiusArray, mMockRoundedCornersRadiusArray);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.dimen.rounded_corner_radius, fillValue);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.array.config_roundedCornerTopRadiusArray, mMockRoundedCornersRadiusArray);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.dimen.rounded_corner_radius_top, fillValue);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.array.config_roundedCornerBottomRadiusArray, mMockRoundedCornersRadiusArray);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.dimen.rounded_corner_radius_bottom, fillValue);
+    }
+
+
     private DesktopModeWindowDecoration createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo) {
         return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 46a0b99..0f6cbff 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2499,14 +2499,12 @@
      * </ul>
      */
     public static int getPlatformType(Context context) {
-        if (((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
-                .isVoiceCapable()) {
+        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            return PLATFORM_AUTOMOTIVE;
+        } else if ((context.getSystemService(TelephonyManager.class)).isVoiceCapable()) {
             return PLATFORM_VOICE;
         } else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
             return PLATFORM_TELEVISION;
-        } else if (context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_AUTOMOTIVE)) {
-            return PLATFORM_AUTOMOTIVE;
         } else {
             return PLATFORM_DEFAULT;
         }
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index e0df794..193728a 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -18,21 +18,21 @@
     AImageDecoder_getRepeatCount; # introduced=31
     AImageDecoder_advanceFrame; # introduced=31
     AImageDecoder_rewind; # introduced=31
-    AImageDecoder_getFrameInfo; # introduced = 31
-    AImageDecoder_setInternallyHandleDisposePrevious; # introduced = 31
+    AImageDecoder_getFrameInfo; # introduced=31
+    AImageDecoder_setInternallyHandleDisposePrevious; # introduced=31
     AImageDecoderHeaderInfo_getWidth; # introduced=30
     AImageDecoderHeaderInfo_getHeight; # introduced=30
     AImageDecoderHeaderInfo_getMimeType; # introduced=30
     AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30
     AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30
     AImageDecoderHeaderInfo_getDataSpace; # introduced=30
-    AImageDecoderFrameInfo_create; # introduced = 31
-    AImageDecoderFrameInfo_delete; # introduced = 31
-    AImageDecoderFrameInfo_getDuration; # introduced = 31
-    AImageDecoderFrameInfo_getFrameRect; # introduced = 31
-    AImageDecoderFrameInfo_hasAlphaWithinBounds; # introduced = 31
-    AImageDecoderFrameInfo_getDisposeOp; # introduced = 31
-    AImageDecoderFrameInfo_getBlendOp; # introduced = 31
+    AImageDecoderFrameInfo_create; # introduced=31
+    AImageDecoderFrameInfo_delete; # introduced=31
+    AImageDecoderFrameInfo_getDuration; # introduced=31
+    AImageDecoderFrameInfo_getFrameRect; # introduced=31
+    AImageDecoderFrameInfo_hasAlphaWithinBounds; # introduced=31
+    AImageDecoderFrameInfo_getDisposeOp; # introduced=31
+    AImageDecoderFrameInfo_getBlendOp; # introduced=31
     AndroidBitmap_getInfo;
     AndroidBitmap_getDataSpace;
     AndroidBitmap_lockPixels;
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 0ff1c7f..dfa5735 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -19,16 +19,18 @@
 import android.R
 import android.app.assist.AssistStructure
 import android.content.Context
-import android.credentials.CredentialManager
-import android.credentials.CredentialOption
-import android.credentials.GetCandidateCredentialsException
-import android.credentials.GetCandidateCredentialsResponse
+import android.app.PendingIntent
+import android.credentials.GetCredentialResponse
 import android.credentials.GetCredentialRequest
-import android.credentials.ui.GetCredentialProviderData
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCandidateCredentialsException
+import android.credentials.CredentialOption
 import android.graphics.drawable.Icon
+import android.credentials.ui.GetCredentialProviderData
 import android.os.Bundle
 import android.os.CancellationSignal
 import android.os.OutcomeReceiver
+import android.credentials.Credential
 import android.service.autofill.AutofillService
 import android.service.autofill.Dataset
 import android.service.autofill.Field
@@ -41,8 +43,11 @@
 import android.service.autofill.SaveRequest
 import android.service.credentials.CredentialProviderService
 import android.util.Log
+import android.view.autofill.AutofillValue
+import android.view.autofill.IAutoFillManagerClient
 import android.view.autofill.AutofillId
 import android.widget.inline.InlinePresentationSpec
+import android.credentials.CredentialManager
 import androidx.autofill.inline.v1.InlineSuggestionUi
 import androidx.credentials.provider.CustomCredentialEntry
 import androidx.credentials.provider.PasswordCredentialEntry
@@ -58,11 +63,13 @@
 import org.json.JSONObject
 import java.util.concurrent.Executors
 
+
 class CredentialAutofillService : AutofillService() {
 
     companion object {
         private const val TAG = "CredAutofill"
 
+        private const val SESSION_ID_KEY = "session_id"
         private const val CRED_HINT_PREFIX = "credential="
         private const val REQUEST_DATA_KEY = "requestData"
         private const val CANDIDATE_DATA_KEY = "candidateQueryData"
@@ -77,10 +84,27 @@
             cancellationSignal: CancellationSignal,
             callback: FillCallback
     ) {
+    }
+
+    override fun onFillCredentialRequest(
+            request: FillRequest,
+            cancellationSignal: CancellationSignal,
+            callback: FillCallback,
+            autofillCallback: IAutoFillManagerClient
+    ) {
         val context = request.fillContexts
         val structure = context[context.size - 1].structure
         val callingPackage = structure.activityComponent.packageName
-        Log.i(TAG, "onFillRequest called for $callingPackage")
+        Log.i(TAG, "onFillCredentialRequest called for $callingPackage")
+
+        var sessionId = request.clientState?.getInt(SESSION_ID_KEY)
+
+        Log.i(TAG, "Autofill sessionId: " + sessionId)
+        if (sessionId == null) {
+            Log.i(TAG, "Session Id not found")
+            callback.onFailure("Session Id not found")
+            return
+        }
 
         val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
         if (getCredRequest == null) {
@@ -95,7 +119,31 @@
                 GetCandidateCredentialsException> {
             override fun onResult(result: GetCandidateCredentialsResponse) {
                 Log.i(TAG, "getCandidateCredentials onResponse")
-                val fillResponse = convertToFillResponse(result, request)
+
+                if (result.getCredentialResponse != null) {
+                    val autofillId: AutofillId? = result.getCredentialResponse
+                            .credential.data.getParcelable(
+                                    CredentialProviderService.EXTRA_AUTOFILL_ID,
+                                    AutofillId::class.java)
+                    Log.i(TAG, "getCandidateCredentials final response, autofillId: " +
+                            autofillId)
+
+                    if (autofillId != null) {
+                        autofillCallback.autofill(
+                                sessionId,
+                                mutableListOf(autofillId),
+                                mutableListOf(
+                                        AutofillValue.forText(
+                                                convertResponseToJson(result.getCredentialResponse)
+                                        )
+                                ),
+                                false)
+                    }
+                    return
+                }
+
+                val fillResponse = convertToFillResponse(result, request,
+                        this@CredentialAutofillService)
                 if (fillResponse != null) {
                     callback.onSuccess(fillResponse)
                 } else {
@@ -115,10 +163,62 @@
                 callingPackage,
                 CancellationSignal(),
                 Executors.newSingleThreadExecutor(),
-                outcome
+                outcome,
+                autofillCallback
         )
     }
 
+    // TODO(b/318118018): Use from Jetpack
+    private fun convertResponseToJson(response: GetCredentialResponse): String? {
+        try {
+            val jsonObject = JSONObject()
+            jsonObject.put("type", "get")
+            val jsonCred = JSONObject()
+            jsonCred.put("type", response.credential.type)
+            jsonCred.put("data", credentialToJSON(
+                    response.credential))
+            jsonObject.put("credential", jsonCred)
+            return jsonObject.toString()
+        } catch (e: JSONException) {
+            Log.i(
+                    TAG, "Exception while constructing response JSON: " +
+                    e.message
+            )
+        }
+        return null
+    }
+
+    // TODO(b/318118018): Replace with calls to Jetpack
+    private fun credentialToJSON(credential: Credential): JSONObject? {
+        Log.i(TAG, "credentialToJSON")
+        try {
+            if (credential.type == "android.credentials.TYPE_PASSWORD_CREDENTIAL") {
+                Log.i(TAG, "toJSON PasswordCredential")
+
+                val json = JSONObject()
+                val id = credential.data.getString("androidx.credentials.BUNDLE_KEY_ID")
+                val pass = credential.data.getString("androidx.credentials.BUNDLE_KEY_PASSWORD")
+                json.put("androidx.credentials.BUNDLE_KEY_ID", id)
+                json.put("androidx.credentials.BUNDLE_KEY_PASSWORD", pass)
+                return json
+            } else if (credential.type == "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL") {
+                Log.i(TAG, "toJSON PublicKeyCredential")
+
+                val json = JSONObject()
+                val responseJson = credential
+                        .data
+                        .getString("androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON")
+                json.put("androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON",
+                        responseJson)
+                return json
+            }
+        } catch (e: JSONException) {
+            Log.i(TAG, "issue while converting credential response to JSON")
+        }
+        Log.i(TAG, "Unsupported credential type")
+        return null
+    }
+
     private fun getEntryToIconMap(
             candidateProviderDataList: MutableList<GetCredentialProviderData>
     ): Map<String, Icon> {
@@ -150,14 +250,16 @@
 
     private fun convertToFillResponse(
             getCredResponse: GetCandidateCredentialsResponse,
-            filLRequest: FillRequest
+            filLRequest: FillRequest,
+            context: Context
     ): FillResponse? {
         val providerList = GetFlowUtils.toProviderList(
                 getCredResponse.candidateProviderDataList,
-                this@CredentialAutofillService)
+                context)
         if (providerList.isEmpty()) {
             return null
         }
+
         val entryIconMap: Map<String, Icon> =
                 getEntryToIconMap(getCredResponse.candidateProviderDataList)
         val autofillIdToProvidersMap: Map<AutofillId, List<ProviderInfo>> =
@@ -166,7 +268,8 @@
         var validFillResponse = false
         autofillIdToProvidersMap.forEach { (autofillId, providers) ->
             validFillResponse = processProvidersForAutofillId(
-                    filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder)
+                    filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder,
+                    getCredResponse.pendingIntent)
                     .or(validFillResponse)
         }
         if (!validFillResponse) {
@@ -180,7 +283,8 @@
             autofillId: AutofillId,
             providerList: List<ProviderInfo>,
             entryIconMap: Map<String, Icon>,
-            fillResponseBuilder: FillResponse.Builder
+            fillResponseBuilder: FillResponse.Builder,
+            bottomSheetPendingIntent: PendingIntent?
     ): Boolean {
         if (providerList.isEmpty()) {
             return false
@@ -227,9 +331,9 @@
                         ?: getDefaultIcon()
             }
             // Create inline presentation
-            var inlinePresentation: InlinePresentation? = null;
+            var inlinePresentation: InlinePresentation? = null
+            var spec: InlinePresentationSpec?
             if (inlinePresentationSpecs != null) {
-                val spec: InlinePresentationSpec
                 if (i < inlinePresentationSpecsCount) {
                     spec = inlinePresentationSpecs[i]
                 } else {
@@ -265,9 +369,52 @@
                             .build())
             datasetAdded = true
         }
+        val pinnedSpec = getLastInlinePresentationSpec(inlinePresentationSpecs,
+                inlinePresentationSpecsCount)
+        if (datasetAdded && bottomSheetPendingIntent != null && pinnedSpec != null) {
+            addPinnedInlineSuggestion(bottomSheetPendingIntent, pinnedSpec, autofillId,
+                    fillResponseBuilder)
+        }
         return datasetAdded
     }
 
+    private fun getLastInlinePresentationSpec(
+            inlinePresentationSpecs: List<InlinePresentationSpec>?,
+            inlinePresentationSpecsCount: Int
+    ): InlinePresentationSpec? {
+        if (inlinePresentationSpecs != null) {
+            return inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
+        }
+        return null
+    }
+
+    private fun addPinnedInlineSuggestion(
+            bottomSheetPendingIntent: PendingIntent,
+            spec: InlinePresentationSpec,
+            autofillId: AutofillId,
+            fillResponseBuilder: FillResponse.Builder
+    ) {
+        val dataSetBuilder = Dataset.Builder()
+        val sliceBuilder = InlineSuggestionUi
+                .newContentBuilder(bottomSheetPendingIntent)
+                .setStartIcon(Icon.createWithResource(this,
+                        com.android.credentialmanager.R.drawable.ic_other_sign_in_24))
+        val presentationBuilder = Presentations.Builder()
+                .setInlinePresentation(InlinePresentation(
+                        sliceBuilder.build().slice, spec, /* pinned= */ true))
+
+        fillResponseBuilder.addDataset(
+                dataSetBuilder
+                        .setField(
+                                autofillId,
+                                Field.Builder().setPresentations(
+                                        presentationBuilder.build())
+                                        .build())
+                        .setAuthentication(bottomSheetPendingIntent.intentSender)
+                        .build()
+        )
+    }
+
     /**
      *  Maps Autofill Id to provider list. For example, passing in a provider info
      *
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 14bcac2..558a910 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -303,3 +303,10 @@
    description: "Displays the auto on toggle in the bluetooth QS tile dialog"
    bug: "316985153"
 }
+
+flag {
+   name: "smartspace_relocate_to_bottom"
+   namespace: "systemui"
+   description: "Relocate Smartspace to bottom of the Lock Screen"
+   bug: "316212788"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 55fc3a2..17c4e02 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -194,6 +194,7 @@
         items(
             count = list.size,
             key = { index -> list[index].key },
+            contentType = { index -> list[index].key },
             span = { index -> GridItemSpan(list[index].size.span) },
         ) { index ->
             val cardModifier = Modifier.width(Dimensions.CardWidth)
@@ -361,6 +362,8 @@
                     .createView(context, model.appWidgetId, model.providerInfo)
                     .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
             },
+            // For reusing composition in lazy lists.
+            onReset = {},
         )
     }
 }
@@ -376,7 +379,7 @@
             FrameLayout(context).apply { addView(model.remoteViews.apply(context, this)) }
         },
         // For reusing composition in lazy lists.
-        onReset = {}
+        onReset = {},
     )
 }
 
diff --git a/packages/SystemUI/docs/imgs/ribbon.png b/packages/SystemUI/docs/imgs/ribbon.png
new file mode 100644
index 0000000..9f57652
--- /dev/null
+++ b/packages/SystemUI/docs/imgs/ribbon.png
Binary files differ
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
new file mode 100644
index 0000000..3e4a1b4
--- /dev/null
+++ b/packages/SystemUI/docs/scene.md
@@ -0,0 +1,297 @@
+# The Scene Framework
+
+Known internally as "Flexiglass", this framework defines a graph where each node
+is a "scene" and each edge between the scenes is a transition. The scenes are
+the main components of System UI, on phones these are: the lockscreen, bouncer,
+shade, and quick settings panels/views/screens). Each scene is a standalone
+experience.
+
+The **main goal** of the framework is to increase code health by applying
+[Separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns)
+over several dimensions:
+
+1.  Each scene is a standalone piece of UI; their code doesn't need to concern
+    itself with either transition animations or anything in other scenes. This
+    frees the developer to be able to focus only on the content of the UI for
+    that scene.
+2.  Transition definitions (which scene leads to which other scene following
+    which user action) are pulled out and separated from the content of the UI.
+3.  Transition animations (the effects that happen alongside the gradual change
+    from one scene to another) are also pulled out and separated from the
+    content of the UI.
+
+In addition to the above, some of the **secondary goals** are: 4. Make
+**customization easier**: by separating scenes to standalone pieces, it becomes
+possible for variant owners and OEMs to exclude or replace certain scenes or to
+add brand-new scenes. 5. **Enable modularization**: by separating scenes to
+standalone pieces, it becomes possible to break down System UI into smaller
+codebases, each one of which could be built on its own. Note: this isn't part of
+the scene framework itself but is something that can be done more easily once
+the scene framework is in place.
+
+## Terminology
+
+*   **Scene** a collection of UI elements in a layout that, together, make up a
+    "screen" or "page" that is as large as the container. Scenes can be
+    navigated between / transition to/from. To learn more, please see
+    [this section](#Defining-a-scene).
+*   **Element** (or "UI element") a single unit of UI within a scene. One scene
+    can arrange multiple elements within a layout structure.
+*   **Transition** the gradual switching from one scene to another scene. There
+    are two kinds: [user-driven](Scene-navigation) and
+    [automatic](Automatic-scene-transitions) scene transitions.
+*   **Transition animation** the set of UI effects that occurs while/during a
+    transition. These can apply to the entire scene or to specific elements in
+    the scene. To learn more, please see
+    [this section](#Scene-transition-animations).
+*   **Scene container** (or just "container") the root piece of UI (typically a
+    `@Composable` function) that sets up all the scenes, their transitions, etc.
+    To learn more, please see [this section](#Scene-container).
+*   **Container configuration** (or just "configuration") the collection of
+    scenes and some added information about the desired behaviour of a
+    container. To learn more, please see
+    [this section](#Scene-container-configuration).
+
+## Enabling the framework
+
+As of the end of 2023, the scene framework is under development; as such, it is
+disabled by default. For those who are interested in a preview, please follow
+the instructions below to turn it on.
+
+NOTE: in case these instructions become stale and don't actually enable the
+framework, please make sure `SceneContainerFlag.isEnabled` in the
+[`SceneContainerFlags.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt)
+file evalutes to `true`.
+
+1.  Set **`SCENE_CONTAINER_ENABLED`** to `true` in the
+    [`Flags.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/flags/Flags.kt)
+    file
+2.  Set the **`migrate_keyguard_status_bar_view`** classic flag to `true` by
+    running: `console $ adb shell statusbar cmd migrate_keyguard_status_bar_view
+    true`
+3.  Set a collection of **aconfig flags** to `true` by running the following
+    commands: `console $ adb shell device_config put systemui
+    com.android.systemui.scene_container true $ adb shell device_config put
+    systemui com.android.systemui.keyguard_bottom_area_refactor true $ adb shell
+    device_config put systemui
+    com.android.systemui.keyguard_shade_migration_nssl true $ adb shell
+    device_config put systemui com.android.systemui.media_in_scene_container
+    true`
+4.  **Restart** System UI by issuing the following command: `console $ adb shell
+    am crash com.android.systemui`
+5.  **Verify** that the scene framework was turned on. There are two ways to do
+    this:
+
+    *(a)* look for the sash/ribbon UI at the bottom-right corner of the display:
+    ![ribbon](imgs/ribbon.png)
+
+    NOTE: this will be removed proper to the actual release of the framework.
+
+    *(b)* Turn on logging and look for the logging statements in `logcat`:
+    ```console
+
+    # Turn on logging from the framework:
+
+    $ adb shell cmd statusbar echo -b SceneFramework:verbose
+
+# Look for the log statements from the framework:
+
+$ adb logcat -v time SceneFramework:* *:S ```
+
+To **disable** the framework, simply turn off the main aconfig flag: `console $
+adb shell device_config put systemui com.android.systemui.scene_container false`
+
+## Defining a scene
+
+Each scene is defined as an implementation of the
+[`ComposableScene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt)
+interface, which has three parts: 1. The `key` property returns the
+[`SceneKey`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt)
+that uniquely identifies that scene 2. The `destinationScenes` `Flow` returns
+the (potentially ever-changing) set of navigation edges to other scenes, based
+on user-actions, which is how the navigation graph is defined (see
+[the Scene navigation](#Scene-navigation) section for more) 3. The `Content`
+function which uses
+[Jetpack Compose](https://developer.android.com/jetpack/compose) to declare of
+the UI itself. This is the UI "at rest", e.g. once there is no transition
+between any two scenes. The Scene Framework has other ways to define how the
+content of your UI changes with and throughout a transition to learn more please
+see the [Scene transition animations](#Scene-transition-animations) section
+
+For example: ```kotlin @SysUISingleton class YourScene @Inject constructor( //
+your dependencies here ) : ComposableScene { override val key =
+SceneKey.YourScene
+
+```
+override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
+    MutableStateFlow<Map<UserAction, SceneModel>>(
+        mapOf(
+            // This is where scene navigation is defined, more on that below.
+        )
+    ).asStateFlow()
+
+@Composable
+override fun SceneScope.Content(
+    modifier: Modifier,
+) {
+    // This is where the UI is defined using Jetpack Compose.
+}
+```
+
+} ```
+
+### Injecting scenes
+
+Scenes are injected into the Dagger dependency graph from the
+[`SceneModule`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt;l=35-50;drc=564f233d5b597aedf06961c76e582464eebe8ba6).
+
+## Scene navigation
+
+As seen above, each scene is responsible for providing an observable `Flow` of a
+`Map` that connects `UserAction` (for example: swipe down, swipe up, back
+button/gesture, etc.) keys to `SceneModel` destinations. This is how the scene
+navigation graph is defined.
+
+NOTE: this controls *only* user-input based navigation. To learn about the other
+type of scene navigation, please see the
+[Automatic scene transitions](#Automatic-scene-transitions) section.
+
+Because this is a `Flow`, scene implemetations should feel free to emit new
+values over time. For example, the `Lockscreen` scene ties the "swipe up" user
+action to go to the `Bouncer` scene if the device is still locked or to go to
+the `Gone` scene if the device is unlocked, allowing the user to dismiss the
+lockscreen UI when not locked.
+
+## Scene transition animations
+
+The Scene Framework separates transition animations from content UI declaration
+by placing the definition of the former in a different location. This way,
+there's no longer a need to contaminate the content UI declaration with
+animation logic, a practice that becomes unscalable over time.
+
+Under the hood, the Scene Framework uses
+[`SceneTransitionLayout`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt),
+a `@Composable` function designed with scene graph and transitions in mind. In
+fact, the Scene Framework is merely a shallow wrapper around
+`SceneTransitionLayout`.
+
+The `SceneTransitionLayout` API requires the transitions to be passed-in
+separately from the scenes themselves. In System UI, the transitions can be
+found in
+[`SceneContainerTransitions`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt).
+As you can see, each possible scene-to-scene transition has its own builder,
+here's one example:
+
+```kotlin
+fun TransitionBuilder.lockscreenToShadeTransition() {
+    spec = tween(durationMillis = 500)
+
+    punchHole(Shade.Elements.QuickSettings, bounds = Shade.Elements.Scrim, Shade.Shapes.Scrim)
+    translate(Shade.Elements.Scrim, Edge.Top, startsOutsideLayoutBounds = false)
+    fractionRange(end = 0.5f) {
+        fade(Shade.Elements.ScrimBackground)
+        translate(
+            QuickSettings.Elements.CollapsedGrid,
+            Edge.Top,
+            startsOutsideLayoutBounds = false,
+        )
+    }
+    fractionRange(start = 0.5f) { fade(Notifications.Elements.Notifications) }
+}
+```
+
+Going through the example code: * The `spec` is the animation that should be
+invoked, in the example above, we use a `tween` animation with a duration of 500
+milliseconds * Then there's a series of function calls: `punchHole` applies a
+clip mask to the `Scrim` element in the destination scene (in this case it's the
+`Shade` scene) which has the position and size determined by the `bounds`
+parameter and the shape passed into the `shape` parameter. This lets the
+`Lockscreen` scene render "through" the `Shade` scene * The `translate` call
+shifts the `Scrim` element to/from the `Top` edge of the scene container * The
+first `fractionRange` wrapper tells the system to apply its contained functions
+only during the first half of the transition. Inside of it, we see a `fade` of
+the `ScrimBackground` element and a `translate` o the `CollpasedGrid` element
+to/from the `Top` edge * The second `fractionRange` only starts at the second
+half of the transition (e.g. when the previous one ends) and applies a `fade` on
+the `Notifications` element
+
+You can find the actual documentation for this API
+[here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt).
+
+### Tagging elements
+
+As demonstrated above, elements within a scene can be addressed from transition
+defintions. In order to "tag" an element with a specific `ElementKey`, the
+[`element` modifier](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt)
+must be used on the composable that declared that element's UI:
+
+```kotlin
+Text(
+    text = "Some text",
+    modifier = Modifier.element(MyElements.SomeText),
+)
+```
+
+In addition to the ability to refer to a tagged element in transition
+definitions, if the same `ElementKey` is used for one element in the current
+scene and another element in the destination scene, the element is considered to
+be a **shared element**. As such, the framework automatically translates and
+scales the bounds of the shared element from its current bounds in the source
+scene to its final bounds in the destination scene.
+
+## Scene container
+
+To set up a scene framework instance, a scene container must be declared. This
+is the root of an entire scene graph that puts together the scenes, their
+transitions, and the configuration. The container is then added to a parent
+`@Composable` or `View` so it can be displayed.
+
+The default scene container in System UI is defined in the
+[`SceneContainer.kt` file](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt).
+
+### Scene container configuration
+
+The `SceneContainer` function is passed a few parameters including a view-model
+and a set of scenes. The exact details of what gets passed in depends on the
+[`SceneContainerConfig` object](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt)
+which is injected into the Dagger dependency graph
+[here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt).
+
+## Automatic scene transitions
+
+The scene framework supports the ability for scenes to change automatically
+based on device state or events other than direct user input. For example: when
+the device is locked, there's an automatic scene transition to the `Lockscreen`
+scene.
+
+This logic is contained within the
+[`SceneContainerStartable`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt)
+class.
+
+## Side-effects
+
+Similarly to [the above](#Automatic-scene-transitions), the
+`SceneContainerStartable` also handles side-effects by updating other parts of
+the System UI codebase whenever internal scene framework state changes. As an
+example: the visibility of the `View` that contains our
+[scene container](#Scene-container) is updated every time there's a transition
+to or from the `Gone` scene.
+
+## Observing scene transition state
+
+There are a couple of ways to observe the transition state:
+
+1.  [Easiest] using the `SceneScope` of the scene container, simply use the
+    `animateSharedXAsState` API, the full list is
+    [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateSharedAsState.kt).
+2.  [Harder] if outside the `SceneScope` of the scene container, observe
+    [`SceneInteractor.transitionState`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt;l=88;drc=af57d5e49431c6728e7cf192bada88e0541ebf0c).
+
+## Dependency Injection
+
+The entire framework is provided into the Dagger dependency graph from the
+top-level Dagger module at
+[`SceneContainerFrameworkModule`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt)
+this puts together the scenes from `SceneModule`, the configuration from
+`SceneContainerConfigModule`, and the startable from
+`SceneContainerStartableModule`.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 1f8e29a..62084aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -28,6 +28,7 @@
 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.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -169,6 +170,109 @@
         }
 
     @Test
+    fun smartspaceDynamicSizing_oneCard_fullSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 1,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.FULL,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_twoCards_halfSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 2,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.HALF,
+                    CommunalContentSize.HALF,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_threeCards_thirdSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 3,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_fourCards_oneFullAndThreeThirdSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 4,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.FULL,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_fiveCards_twoHalfAndThreeThirdSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 5,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.HALF,
+                    CommunalContentSize.HALF,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                )
+        )
+
+    @Test
+    fun smartspace_dynamicSizing_sixCards_allThirdSize() =
+        testSmartspaceDynamicSizing(
+            totalTargets = 6,
+            expectedSizes =
+                listOf(
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                    CommunalContentSize.THIRD,
+                )
+        )
+
+    private fun testSmartspaceDynamicSizing(
+        totalTargets: Int,
+        expectedSizes: List<CommunalContentSize>,
+    ) =
+        testScope.runTest {
+            // Keyguard showing, and tutorial completed.
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            val targets = mutableListOf<SmartspaceTarget>()
+            for (index in 0 until totalTargets) {
+                val target = mock(SmartspaceTarget::class.java)
+                whenever(target.smartspaceTargetId).thenReturn("target$index")
+                whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+                whenever(target.remoteViews).thenReturn(mock(RemoteViews::class.java))
+                targets.add(target)
+            }
+
+            smartspaceRepository.setCommunalSmartspaceTargets(targets)
+
+            val smartspaceContent by collectLastValue(underTest.smartspaceContent)
+            assertThat(smartspaceContent?.size).isEqualTo(totalTargets)
+            for (index in 0 until totalTargets) {
+                assertThat(smartspaceContent?.get(index)?.size).isEqualTo(expectedSizes[index])
+            }
+        }
+
+    @Test
     fun umo_mediaPlaying_showsUmo() =
         testScope.runTest {
             // Tutorial completed.
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 0f4e583..18fb895 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
@@ -24,6 +24,9 @@
 import com.android.systemui.communal.data.repository.CommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalContentSize.FULL
+import com.android.systemui.communal.shared.model.CommunalContentSize.HALF
+import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -133,12 +136,11 @@
                         target.featureType == SmartspaceTarget.FEATURE_TIMER &&
                             target.remoteViews != null
                     }
-                    .map Target@{ target ->
+                    .mapIndexed Target@{ index, target ->
                         return@Target CommunalContentModel.Smartspace(
                             smartspaceTargetId = target.smartspaceTargetId,
                             remoteViews = target.remoteViews!!,
-                            // Smartspace always as HALF for now.
-                            size = CommunalContentSize.HALF,
+                            size = dynamicContentSize(targets.size, index),
                         )
                     }
             }
@@ -147,23 +149,50 @@
     /** A list of tutorial content to be displayed in the communal hub in tutorial mode. */
     val tutorialContent: List<CommunalContentModel.Tutorial> =
         listOf(
-            CommunalContentModel.Tutorial(id = 0, CommunalContentSize.FULL),
-            CommunalContentModel.Tutorial(id = 1, CommunalContentSize.THIRD),
-            CommunalContentModel.Tutorial(id = 2, CommunalContentSize.THIRD),
-            CommunalContentModel.Tutorial(id = 3, CommunalContentSize.THIRD),
-            CommunalContentModel.Tutorial(id = 4, CommunalContentSize.HALF),
-            CommunalContentModel.Tutorial(id = 5, CommunalContentSize.HALF),
-            CommunalContentModel.Tutorial(id = 6, CommunalContentSize.HALF),
-            CommunalContentModel.Tutorial(id = 7, CommunalContentSize.HALF),
+            CommunalContentModel.Tutorial(id = 0, FULL),
+            CommunalContentModel.Tutorial(id = 1, THIRD),
+            CommunalContentModel.Tutorial(id = 2, THIRD),
+            CommunalContentModel.Tutorial(id = 3, THIRD),
+            CommunalContentModel.Tutorial(id = 4, HALF),
+            CommunalContentModel.Tutorial(id = 5, HALF),
+            CommunalContentModel.Tutorial(id = 6, HALF),
+            CommunalContentModel.Tutorial(id = 7, HALF),
         )
 
     val umoContent: Flow<List<CommunalContentModel.Umo>> =
         mediaRepository.mediaPlaying.flatMapLatest { mediaPlaying ->
             if (mediaPlaying) {
                 // TODO(b/310254801): support HALF and FULL layouts
-                flowOf(listOf(CommunalContentModel.Umo(CommunalContentSize.THIRD)))
+                flowOf(listOf(CommunalContentModel.Umo(THIRD)))
             } else {
                 flowOf(emptyList())
             }
         }
+
+    companion object {
+        /**
+         * Calculates the content size dynamically based on the total number of contents of that
+         * type.
+         *
+         * Contents with the same type are expected to fill each column evenly. Currently there are
+         * three possible sizes. When the total number is 1, size for that content is [FULL], when
+         * the total number is 2, size for each is [HALF], and 3, size for each is [THIRD].
+         *
+         * When dynamic contents fill in multiple columns, the first column follows the algorithm
+         * above, and the remaining contents are packed in [THIRD]s. For example, when the total
+         * number if 4, the first one is [FULL], filling the column, and the remaining 3 are
+         * [THIRD].
+         *
+         * @param size The total number of contents of this type.
+         * @param index The index of the current content of this type.
+         */
+        private fun dynamicContentSize(size: Int, index: Int): CommunalContentSize {
+            val remainder = size % CommunalContentSize.entries.size
+            return CommunalContentSize.toSize(
+                span =
+                    FULL.span /
+                        if (index > remainder - 1) CommunalContentSize.entries.size else remainder
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
index c903709..572794d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
@@ -30,5 +30,13 @@
     HALF(3),
 
     /** Content takes a third of the height of the column. */
-    THIRD(2),
+    THIRD(2);
+
+    companion object {
+        /** Converts from span to communal content size. */
+        fun toSize(span: Int): CommunalContentSize {
+            return entries.find { it.span == span }
+                ?: throw Exception("Invalid span for communal content size")
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index ab69acb..3be60b7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -157,7 +157,10 @@
         // If the hub is fully visible, send all touch events to it.
         val communalVisible = hubShowing && !hubOccluded
         if (communalVisible) {
-            return communalContainerView.dispatchTouchEvent(ev)
+            communalContainerView.dispatchTouchEvent(ev)
+            // Return true regardless of dispatch result as some touches at the start of a gesture
+            // may return false from dispatchTouchEvent.
+            return true
         }
 
         if (edgeSwipeRegionWidth == 0) {
@@ -172,13 +175,19 @@
                 x >= communalContainerView.width - edgeSwipeRegionWidth
             if (inOpeningSwipeRegion && !hubOccluded) {
                 isTrackingOpenGesture = true
-                return communalContainerView.dispatchTouchEvent(ev)
+                communalContainerView.dispatchTouchEvent(ev)
+                // Return true regardless of dispatch result as some touches at the start of a
+                // gesture may return false from dispatchTouchEvent.
+                return true
             }
         } else if (isTrackingOpenGesture) {
             if (isUp || isCancel) {
                 isTrackingOpenGesture = false
             }
-            return communalContainerView.dispatchTouchEvent(ev)
+            communalContainerView.dispatchTouchEvent(ev)
+            // Return true regardless of dispatch result as some touches at the start of a gesture
+            // may return false from dispatchTouchEvent.
+            return true
         }
 
         return false
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 4688658..6d0915b 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -38,6 +38,7 @@
 import android.service.autofill.SaveRequest;
 import android.text.format.DateUtils;
 import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
 
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.infra.ServiceConnector;
@@ -56,12 +57,22 @@
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
+    private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME =
+            new ComponentName("com.android.credentialmanager",
+                    "com.android.credentialmanager.autofill.CredentialAutofillService");
+
     private final FillServiceCallbacks mCallbacks;
     private final Object mLock = new Object();
     private CompletableFuture<FillResponse> mPendingFillRequest;
     private int mPendingFillRequestId = INVALID_REQUEST_ID;
     private final ComponentName mComponentName;
 
+    private final boolean mIsCredentialAutofillService;
+
+    public boolean isCredentialAutofillService() {
+        return mIsCredentialAutofillService;
+    }
+
     public interface FillServiceCallbacks
             extends AbstractRemoteService.VultureCallback<RemoteFillService> {
         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@@ -83,6 +94,7 @@
                 userId, IAutoFillService.Stub::asInterface);
         mCallbacks = callbacks;
         mComponentName = componentName;
+        mIsCredentialAutofillService = mComponentName.equals(CREDMAN_SERVICE_COMPONENT_NAME);
     }
 
     @Override // from ServiceConnector.Impl
@@ -117,6 +129,10 @@
         super.addLast(iAutoFillServiceJob);
     }
 
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
     /**
      * Cancel the currently pending request.
      *
@@ -134,6 +150,78 @@
         }
     }
 
+    public void onFillCredentialRequest(@NonNull FillRequest request,
+            IAutoFillManagerClient autofillCallback) {
+        if (sVerbose) {
+            Slog.v(TAG, "onFillRequest:" + request);
+        }
+        AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+        AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>();
+
+        CompletableFuture<FillResponse> connectThenFillRequest = postAsync(remoteService -> {
+            if (sVerbose) {
+                Slog.v(TAG, "calling onFillRequest() for id=" + request.getId());
+            }
+
+            CompletableFuture<FillResponse> fillRequest = new CompletableFuture<>();
+            remoteService.onFillCredentialRequest(request, new IFillCallback.Stub() {
+                @Override
+                public void onCancellable(ICancellationSignal cancellation) {
+                    CompletableFuture<FillResponse> future = futureRef.get();
+                    if (future != null && future.isCancelled()) {
+                        dispatchCancellationSignal(cancellation);
+                    } else {
+                        cancellationSink.set(cancellation);
+                    }
+                }
+
+                @Override
+                public void onSuccess(FillResponse response) {
+                    fillRequest.complete(response);
+                }
+
+                @Override
+                public void onFailure(int requestId, CharSequence message) {
+                    String errorMessage = message == null ? "" : String.valueOf(message);
+                    fillRequest.completeExceptionally(
+                            new RuntimeException(errorMessage));
+                }
+            }, autofillCallback);
+            return fillRequest;
+        }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+        futureRef.set(connectThenFillRequest);
+
+        synchronized (mLock) {
+            mPendingFillRequest = connectThenFillRequest;
+            mPendingFillRequestId = request.getId();
+        }
+
+        connectThenFillRequest.whenComplete((res, err) -> Handler.getMain().post(() -> {
+            synchronized (mLock) {
+                mPendingFillRequest = null;
+                mPendingFillRequestId = INVALID_REQUEST_ID;
+            }
+            if (mCallbacks == null) {
+                Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
+                return;
+            }
+            if (err == null) {
+                mCallbacks.onFillRequestSuccess(request.getId(), res,
+                        mComponentName.getPackageName(), request.getFlags());
+            } else {
+                Slog.e(TAG, "Error calling on fill request", err);
+                if (err instanceof TimeoutException) {
+                    dispatchCancellationSignal(cancellationSink.get());
+                    mCallbacks.onFillRequestTimeout(request.getId());
+                } else if (err instanceof CancellationException) {
+                    dispatchCancellationSignal(cancellationSink.get());
+                } else {
+                    mCallbacks.onFillRequestFailure(request.getId(), err.getMessage());
+                }
+            }
+        }));
+    }
+
     public void onFillRequest(@NonNull FillRequest request) {
         if (sVerbose) {
             Slog.v(TAG, "onFillRequest:" + request);
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index 4a6d5c9b..553ba12 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -16,14 +16,19 @@
 
 package com.android.server.autofill;
 
+import static com.android.server.autofill.Session.SESSION_ID_KEY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentSender;
+import android.os.Bundle;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
 import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
 
 /**
  * Requests autofill response from a Remote Autofill Service. This autofill service can be
@@ -95,10 +100,33 @@
     /**
      * Requests a new fill response.
      */
-    public void onFillRequest(FillRequest pendingFillRequest, int flag) {
+    public void onFillRequest(FillRequest pendingFillRequest,
+            InlineSuggestionsRequest pendingInlineSuggestionsRequest, int flag, int id,
+            IAutoFillManagerClient client) {
         Slog.v(TAG, "Requesting fill response to secondary provider.");
         mLastFlag = flag;
-        mRemoteFillService.onFillRequest(pendingFillRequest);
+        if (mRemoteFillService != null && mRemoteFillService.isCredentialAutofillService()) {
+            Slog.v(TAG, "About to call CredAutofill service as secondary provider");
+            addSessionIdToClientState(pendingFillRequest, pendingInlineSuggestionsRequest, id);
+            mRemoteFillService.onFillCredentialRequest(pendingFillRequest, client);
+        } else {
+            mRemoteFillService.onFillRequest(pendingFillRequest);
+        }
+    }
+
+    private FillRequest addSessionIdToClientState(FillRequest pendingFillRequest,
+            InlineSuggestionsRequest pendingInlineSuggestionsRequest, int id) {
+        if (pendingFillRequest.getClientState() == null) {
+            pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
+                    pendingFillRequest.getFillContexts(),
+                    pendingFillRequest.getHints(),
+                    new Bundle(),
+                    pendingFillRequest.getFlags(),
+                    pendingInlineSuggestionsRequest,
+                    pendingFillRequest.getDelayedFillIntentSender());
+        }
+        pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, id);
+        return pendingFillRequest;
     }
 
     public void destroy() {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d71258a..a49f9db 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -234,6 +234,8 @@
             new ComponentName("com.android.credentialmanager",
                     "com.android.credentialmanager.autofill.CredentialAutofillService");
 
+    static final String SESSION_ID_KEY = "session_id";
+
     final Object mLock;
 
     private final AutofillManagerServiceImpl mService;
@@ -383,7 +385,7 @@
      */
     private boolean mHasCallback;
 
-    /** Whether the session has credential provider as the primary provider. */
+    /** Whether the session has credential manager provider as the primary provider. */
     private boolean mIsPrimaryCredential;
 
     @GuardedBy("mLock")
@@ -723,9 +725,16 @@
                     && mSecondaryProviderHandler != null) {
                 Slog.v(TAG, "Requesting fill response to secondary provider.");
                 mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
-                        mPendingFillRequest.getFlags());
+                        mPendingInlineSuggestionsRequest,
+                        mPendingFillRequest.getFlags(), id, mClient);
             } else if (mRemoteFillService != null) {
-                mRemoteFillService.onFillRequest(mPendingFillRequest);
+                if (mIsPrimaryCredential) {
+                    mPendingFillRequest = addSessionIdToClientState(mPendingFillRequest,
+                            mPendingInlineSuggestionsRequest, id);
+                    mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, mClient);
+                } else {
+                    mRemoteFillService.onFillRequest(mPendingFillRequest);
+                }
             }
             mPendingInlineSuggestionsRequest = null;
             mWaitForInlineRequest = false;
@@ -868,6 +877,21 @@
         }
     }
 
+    private FillRequest addSessionIdToClientState(FillRequest pendingFillRequest,
+            InlineSuggestionsRequest pendingInlineSuggestionsRequest, int id) {
+        if (pendingFillRequest.getClientState() == null) {
+            pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
+                    pendingFillRequest.getFillContexts(),
+                    pendingFillRequest.getHints(),
+                    new Bundle(),
+                    pendingFillRequest.getFlags(),
+                    pendingInlineSuggestionsRequest,
+                    pendingFillRequest.getDelayedFillIntentSender());
+        }
+        pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, id);
+        return pendingFillRequest;
+    }
+
     /**
      * Get the list of valid autofill hint types from Device flags
      * Returns empty list if PCC is off or no types available
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 72e62c3..8ad60e6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -243,7 +243,7 @@
     /**
      * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
      */
-    private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+    private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
 
     /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index d0d647c..0185c22 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -163,6 +163,7 @@
         "pixel_audio_android",
         "pixel_bluetooth",
         "pixel_system_sw_touch",
+        "pixel_system_sw_usb",
         "pixel_watch",
         "platform_security",
         "power",
@@ -181,6 +182,7 @@
         "tv_system_ui",
         "usb",
         "vibrator",
+        "virtualization",
         "virtual_devices",
         "wallet_integration",
         "wear_calling_messaging",
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index d80638a..203ac2c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -33,6 +33,7 @@
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_BLUETOOTH_CONNECT;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_CAMERA_SANDBOXED;
 import static android.app.AppOpsManager.OP_FLAGS_ALL;
@@ -2849,6 +2850,11 @@
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (code == OP_BLUETOOTH_CONNECT) {
+                Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as incoming "
+                        + "package: " + packageName + " and uid: " + uid + " is invalid");
+            }
             return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                     packageName);
         }
@@ -2877,6 +2883,13 @@
             }
         } catch (SecurityException e) {
             logVerifyAndGetBypassFailure(uid, e, "noteOperation");
+            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+            if (code == OP_BLUETOOTH_CONNECT) {
+                Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
+                        + " verifyAndGetBypass returned a SecurityException for package: "
+                        + packageName + " and uid: " + uid + " and attributionTag: "
+                        + attributionTag, e);
+            }
             return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                     packageName);
         }
@@ -2890,6 +2903,11 @@
                 if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName + "flags: " +
                         AppOpsManager.flagsToString(flags));
+                // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+                if (code == OP_BLUETOOTH_CONNECT) {
+                    Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
+                            + " #getOpsLocked returned null");
+                }
                 return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                         packageName);
             }
@@ -2930,6 +2948,11 @@
                     attributedOp.rejected(uidState.getState(), flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
                             uidMode);
+                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+                    if (code == OP_BLUETOOTH_CONNECT && uidMode == MODE_ERRORED) {
+                        Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
+                                + " uid mode is MODE_ERRORED");
+                    }
                     return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
                 }
             } else {
@@ -2949,6 +2972,11 @@
                     attributedOp.rejected(uidState.getState(), flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
                             mode);
+                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+                    if (code == OP_BLUETOOTH_CONNECT && mode == MODE_ERRORED) {
+                        Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
+                                + " package mode is MODE_ERRORED");
+                    }
                     return new SyncNotedAppOp(mode, code, attributionTag, packageName);
                 }
             }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 10e6edc..d683855 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1229,11 +1229,6 @@
                         sPlatformPermissions.put(permission, permissionInfo);
                     }
                 } catch (PackageManager.NameNotFoundException ignored) {
-                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                    if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
-                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as package"
-                                + " not found when retrieving permission info");
-                    }
                     return PermissionChecker.PERMISSION_HARD_DENIED;
                 }
             }
@@ -1353,34 +1348,17 @@
                 // way we can avoid the datasource creating an attribution context for every call.
                 if (!(fromDatasource && current.equals(attributionSource))
                         && next != null && !current.isTrusted(context)) {
-                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                    if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
-                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as "
-                                + current + " attribution source isn't a data source and "
-                                + current + " isn't trusted");
-                    }
                     return PermissionChecker.PERMISSION_HARD_DENIED;
                 }
 
                 // If we already checked the permission for this one, skip the work
                 if (!skipCurrentChecks && !checkPermission(context, permissionManagerServiceInt,
                         permission, current)) {
-                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                    if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
-                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as we"
-                                + " aren't skipping permission checks and permission check returns"
-                                + " false for " + current);
-                    }
                     return PermissionChecker.PERMISSION_HARD_DENIED;
                 }
 
                 if (next != null && !checkPermission(context, permissionManagerServiceInt,
                         permission, next)) {
-                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                    if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
-                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
-                                + " permission check returns false for next source " + next);
-                    }
                     return PermissionChecker.PERMISSION_HARD_DENIED;
                 }
 
@@ -1697,12 +1675,6 @@
                 final AttributionSource resolvedAttributionSource = resolveAttributionSource(
                         context, accessorSource);
                 if (resolvedAttributionSource.getPackageName() == null) {
-                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                    if (op == OP_BLUETOOTH_CONNECT) {
-                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as resolved"
-                                + "package name for " + resolvedAttributionSource + " returned"
-                                + " null");
-                    }
                     return AppOpsManager.MODE_ERRORED;
                 }
                 int notedOp = op;
@@ -1716,13 +1688,6 @@
                 if (attributedOp != AppOpsManager.OP_NONE && attributedOp != op) {
                     checkedOpResult = appOpsManager.checkOpNoThrow(op, resolvedAttributionSource);
                     if (checkedOpResult == MODE_ERRORED) {
-                        // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                        if (op == OP_BLUETOOTH_CONNECT) {
-                            Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
-                                    + " checkOp for resolvedAttributionSource "
-                                    + resolvedAttributionSource + " and op " + op
-                                    + " returned MODE_ERRORED");
-                        }
                         return checkedOpResult;
                     }
                     notedOp = attributedOp;
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 686b2a8..dfb5a57 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -63,6 +63,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.autofill.IAutoFillManagerClient;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.credentials.metrics.ApiName;
@@ -483,6 +484,7 @@
         public ICancellationSignal getCandidateCredentials(
                 GetCredentialRequest request,
                 IGetCandidateCredentialsCallback callback,
+                IAutoFillManagerClient clientCallback,
                 final String callingPackage) {
             Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
                     + callingPackage);
@@ -503,7 +505,8 @@
                             request,
                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
                             getEnabledProvidersForUser(userId),
-                            CancellationSignal.fromTransport(cancelTransport)
+                            CancellationSignal.fromTransport(cancelTransport),
+                            clientCallback
                     );
             addSessionLocked(userId, session);
 
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index 6d9b7e8..ca5600e 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -16,6 +16,7 @@
 
 package com.android.server.credentials;
 
+import android.Manifest;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -23,6 +24,7 @@
 import android.credentials.GetCandidateCredentialsException;
 import android.credentials.GetCandidateCredentialsResponse;
 import android.credentials.GetCredentialRequest;
+import android.credentials.GetCredentialResponse;
 import android.credentials.IGetCandidateCredentialsCallback;
 import android.credentials.ui.GetCredentialProviderData;
 import android.credentials.ui.ProviderData;
@@ -30,7 +32,9 @@
 import android.os.CancellationSignal;
 import android.os.RemoteException;
 import android.service.credentials.CallingAppInfo;
+import android.service.credentials.PermissionUtils;
 import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -42,18 +46,22 @@
  */
 public class GetCandidateRequestSession extends RequestSession<GetCredentialRequest,
         IGetCandidateCredentialsCallback, GetCandidateCredentialsResponse>
-        implements ProviderSession.ProviderInternalCallback<GetCandidateCredentialsResponse> {
+        implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> {
     private static final String TAG = "GetCandidateRequestSession";
 
+    private final IAutoFillManagerClient mAutoFillCallback;
+
     public GetCandidateRequestSession(
             Context context, SessionLifetime sessionCallback,
             Object lock, int userId, int callingUid,
             IGetCandidateCredentialsCallback callback, GetCredentialRequest request,
             CallingAppInfo callingAppInfo, Set<ComponentName> enabledProviders,
-            CancellationSignal cancellationSignal) {
+            CancellationSignal cancellationSignal,
+            IAutoFillManagerClient autoFillCallback) {
         super(context, sessionCallback, lock, userId, callingUid, request, callback,
                 RequestInfo.TYPE_GET, callingAppInfo, enabledProviders,
                 cancellationSignal, 0L);
+        mAutoFillCallback = autoFillCallback;
     }
 
     /**
@@ -92,12 +100,25 @@
             return;
         }
 
+        cancelExistingPendingIntent();
+        mPendingIntent = mCredentialManagerUi.createPendingIntent(
+                RequestInfo.newGetRequestInfo(
+                        mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
+                        PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
+                                Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
+                providerDataList);
+
         List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
         for (ProviderData providerData : providerDataList) {
             candidateProviderDataList.add((GetCredentialProviderData) (providerData));
         }
-        respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(
-                candidateProviderDataList));
+
+        try {
+            invokeClientCallbackSuccess(new GetCandidateCredentialsResponse(
+                    candidateProviderDataList, mPendingIntent));
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Issue while responding to client with error : " + e);
+        }
     }
 
     @Override
@@ -151,7 +172,8 @@
 
     @Override
     public void onFinalResponseReceived(ComponentName componentName,
-            GetCandidateCredentialsResponse response) {
-        // Not applicable for session without UI
+            GetCredentialResponse response) {
+        Slog.d(TAG, "onFinalResponseReceived");
+        respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(response));
     }
 }