Merge "Proposal: simplify test with useUnconfinedDispatcher" into main
diff --git a/MEMORY_OWNERS b/MEMORY_OWNERS
index 89ce5140..12aa295 100644
--- a/MEMORY_OWNERS
+++ b/MEMORY_OWNERS
@@ -2,5 +2,4 @@
 tjmercier@google.com
 kaleshsingh@google.com
 jyescas@google.com
-carlosgalo@google.com
 jji@google.com
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 852f047..9c6b71b 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -417,6 +417,7 @@
      *                                  or if any of the output configurations sets a stream use
      *                                  case different from {@link
      *                                  android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT}.
+     * @throws UnsupportedOperationException if the camera has been opened in shared mode
      * @see CameraExtensionCharacteristics#getSupportedExtensions
      * @see CameraExtensionCharacteristics#getExtensionSupportedSizes
      */
@@ -1258,7 +1259,8 @@
      *                                  configurations are empty; or the session configuration
      *                                  executor is invalid;
      *                                  or the output dynamic range combination is
-     *                                  invalid/unsupported.
+     *                                  invalid/unsupported; or the session type is not shared when
+     *                                  camera has been opened in shared mode.
      * @throws CameraAccessException In case the camera device is no longer connected or has
      *                               encountered a fatal error.
      * @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
@@ -1292,6 +1294,8 @@
      * @throws CameraAccessException if the camera device is no longer connected or has
      *                               encountered a fatal error
      * @throws IllegalStateException if the camera device has been closed
+     * @throws UnsupportedOperationException if this is not a primary client of a camera opened in
+     *                                       shared mode
      */
     @NonNull
     public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
@@ -1328,6 +1332,8 @@
      * @throws CameraAccessException if the camera device is no longer connected or has
      *                               encountered a fatal error
      * @throws IllegalStateException if the camera device has been closed
+     * @throws UnsupportedOperationException if this is not a primary client of a camera opened in
+     *                                       shared mode
      *
      * @see #TEMPLATE_PREVIEW
      * @see #TEMPLATE_RECORD
@@ -1369,6 +1375,7 @@
      * @throws CameraAccessException if the camera device is no longer connected or has
      *                               encountered a fatal error
      * @throws IllegalStateException if the camera device has been closed
+     * @throws UnsupportedOperationException if the camera has been opened in shared mode
      *
      * @see CaptureRequest.Builder
      * @see TotalCaptureResult
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index aba2345..bfaff94 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1375,6 +1375,9 @@
      * @throws SecurityException if the application does not have permission to
      *                           access the camera
      *
+     * @throws UnsupportedOperationException if {@link #isCameraDeviceSharingSupported} returns
+     *                                       false for the given {@code cameraId}.
+     *
      * @see #getCameraIdList
      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
      *
@@ -1393,6 +1396,10 @@
         if (executor == null) {
             throw new IllegalArgumentException("executor was null");
         }
+        if (!isCameraDeviceSharingSupported(cameraId)) {
+            throw new UnsupportedOperationException(
+                    "CameraDevice sharing is not supported for Camera ID: " + cameraId);
+        }
         openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0,
                 getRotationOverride(mContext), /*sharedMode*/true);
     }
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 496d316..1c65b08 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -299,6 +299,24 @@
         return mRequestType;
     }
 
+    /**
+     * Get the stream ids corresponding to the target surfaces.
+     *
+     * @hide
+     */
+    public int[] getStreamIds() {
+        return mStreamIdxArray;
+    };
+
+    /**
+     * Get the surface ids corresponding to the target surfaces.
+     *
+     * @hide
+     */
+    public int[] getSurfaceIds() {
+        return mSurfaceIdxArray;
+    };
+
     // If this request is part of constrained high speed request list that was created by
     // {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
     private boolean mIsPartOfCHSRequestList = false;
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index ce8661e..7e0456b 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -340,6 +340,30 @@
         }
     }
 
+    /**
+     * Shared Camera capture session API which can be used by the clients
+     * to start streaming.
+     *
+     * @hide
+     */
+    public int startStreaming(List<Surface> surfaces, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                Log.v(TAG, mIdString + "startStreaming callback " + callback + " executor"
+                        + " " + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.startStreaming(surfaces,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
     private void checkRepeatingRequest(CaptureRequest request) {
         if (request == null) {
             throw new IllegalArgumentException("request must not be null");
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 321f09b..89a6b02 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -451,6 +451,16 @@
         }
     }
 
+    /**
+     * When camera device is opened in shared mode, call to check if this is a primary client.
+     *
+     */
+    public boolean isPrimaryClient() {
+        synchronized (mInterfaceLock) {
+            return mIsPrimaryClient;
+        }
+    }
+
     private Map<String, CameraCharacteristics> getPhysicalIdToChars() {
         if (mPhysicalIdsToChars == null) {
             try {
@@ -858,24 +868,19 @@
         List<SharedOutputConfiguration> sharedConfigs =
                 sharedSessionConfiguration.getOutputStreamsInformation();
         for (SharedOutputConfiguration sharedConfig : sharedConfigs) {
-            if (outConfig.getConfiguredSize().equals(sharedConfig.getSize())
-                    && (outConfig.getConfiguredFormat() == sharedConfig.getFormat())
-                    && (outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
-                    && (outConfig.getSurfaceType() == sharedConfig.getSurfaceType())
+            if ((outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
                     && (outConfig.getMirrorMode() == sharedConfig.getMirrorMode())
-                    && (outConfig.getUsage() == sharedConfig.getUsage())
                     && (outConfig.isReadoutTimestampEnabled()
                     == sharedConfig.isReadoutTimestampEnabled())
                     && (outConfig.getTimestampBase() == sharedConfig.getTimestampBase())
                     && (outConfig.getStreamUseCase() == sharedConfig.getStreamUseCase())
-                    && (outConfig.getColorSpace().equals(
-                    sharedSessionConfiguration.getColorSpace()))
                     && (outConfig.getDynamicRangeProfile()
                     == DynamicRangeProfiles.STANDARD)
-                    && (outConfig.getConfiguredDataspace() == sharedConfig.getDataspace())
                     && (Objects.equals(outConfig.getPhysicalCameraId(),
                     sharedConfig.getPhysicalCameraId()))
                     && (outConfig.getSensorPixelModes().isEmpty())
+                    && (!outConfig.isMultiResolution())
+                    && (!outConfig.isDeferredConfiguration())
                     && (!outConfig.isShared())) {
                 //Found valid config, return true
                 return true;
@@ -907,14 +912,6 @@
         if (config.getExecutor() == null) {
             throw new IllegalArgumentException("Invalid executor");
         }
-        if (mSharedMode) {
-            if (config.getSessionType() != SessionConfiguration.SESSION_SHARED) {
-                throw new IllegalArgumentException("Invalid session type");
-            }
-            if (!checkSharedSessionConfiguration(outputConfigs)) {
-                throw new IllegalArgumentException("Invalid output configurations");
-            }
-        }
         createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
                 config.getStateCallback(), config.getExecutor(), config.getSessionType(),
                 config.getSessionParameters());
@@ -932,17 +929,26 @@
 
             checkIfCameraClosedOrInError();
 
+            boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
+            if (Flags.cameraMultiClient() && mSharedMode) {
+                if (!isSharedSession) {
+                    throw new IllegalArgumentException("Invalid session type");
+                }
+                if (!checkSharedSessionConfiguration(outputConfigurations)) {
+                    throw new IllegalArgumentException("Invalid output configurations");
+                }
+                if (inputConfig != null) {
+                    throw new IllegalArgumentException("Shared capture session doesn't support"
+                            + " input configuration yet.");
+                }
+            }
+
             boolean isConstrainedHighSpeed =
                     (operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
             if (isConstrainedHighSpeed && inputConfig != null) {
                 throw new IllegalArgumentException("Constrained high speed session doesn't support"
                         + " input configuration yet.");
             }
-            boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
-            if (isSharedSession && inputConfig != null) {
-                throw new IllegalArgumentException("Shared capture session doesn't support"
-                        + " input configuration yet.");
-            }
 
             if (mCurrentExtensionSession != null) {
                 mCurrentExtensionSession.commitStats();
@@ -1004,8 +1010,7 @@
                         mCharacteristics);
             } else if (isSharedSession) {
                 newSession = new CameraSharedCaptureSessionImpl(mNextSessionId++,
-                        callback, executor, this, mDeviceExecutor, configureSuccess,
-                        mIsPrimaryClient);
+                        callback, executor, this, mDeviceExecutor, configureSuccess);
             } else {
                 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
                         callback, executor, this, mDeviceExecutor, configureSuccess);
@@ -1074,6 +1079,11 @@
         synchronized(mInterfaceLock) {
             checkIfCameraClosedOrInError();
 
+            if (Flags.cameraMultiClient() && mSharedMode && !mIsPrimaryClient) {
+                throw new UnsupportedOperationException("In shared session mode,"
+                        + "only primary clients can create capture request.");
+            }
+
             for (String physicalId : physicalCameraIdSet) {
                 if (Objects.equals(physicalId, getId())) {
                     throw new IllegalStateException("Physical id matches the logical id!");
@@ -1100,6 +1110,11 @@
         synchronized(mInterfaceLock) {
             checkIfCameraClosedOrInError();
 
+            if (Flags.cameraMultiClient() && mSharedMode && !mIsPrimaryClient) {
+                throw new UnsupportedOperationException("In shared session mode,"
+                        + "only primary clients can create capture request.");
+            }
+
             CameraMetadataNative templatedRequest = null;
 
             templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
@@ -1119,6 +1134,10 @@
             throws CameraAccessException {
         synchronized(mInterfaceLock) {
             checkIfCameraClosedOrInError();
+            if (Flags.cameraMultiClient() && mSharedMode) {
+                throw new UnsupportedOperationException("In shared session mode,"
+                        + "reprocess capture requests are not supported.");
+            }
 
             CameraMetadataNative resultMetadata = new
                     CameraMetadataNative(inputResult.getNativeCopy());
@@ -1572,6 +1591,74 @@
         }
     }
 
+    public int startStreaming(List<Surface> surfaces, CaptureCallback callback,
+            Executor executor) throws CameraAccessException {
+        // Need a valid executor, or current thread needs to have a looper, if
+        // callback is valid
+        executor = checkExecutor(executor, callback);
+        synchronized (mInterfaceLock) {
+            checkIfCameraClosedOrInError();
+            for (Surface surface : surfaces) {
+                if (surface == null) {
+                    throw new IllegalArgumentException("Null Surface targets are not allowed");
+                }
+            }
+            // In shared session mode, if there are other active clients streaming then
+            // stoprepeating does not actually send request to HAL to cancel the request.
+            // Cameraservice will use this call to remove this client surfaces provided in its
+            // previous streaming request. If this is the only client for the shared camera device
+            // then camerservice will ask HAL to cancel the previous repeating request
+            stopRepeating();
+
+            // StartStreaming API does not allow capture parameters to be provided through a capture
+            // request. If the primary client has an existing repeating request, the camera service
+            // will either attach the provided surfaces to that request or create a default capture
+            // request if no repeating request is active. A default capture request is created here
+            // for initial use. The capture callback will provide capture results that include the
+            // actual capture parameters used for the streaming.
+            CaptureRequest.Builder builder = createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+            for (Surface surface : surfaces) {
+                builder.addTarget(surface);
+            }
+            CaptureRequest request = builder.build();
+            request.convertSurfaceToStreamId(mConfiguredOutputs);
+
+            SubmitInfo requestInfo;
+            requestInfo = mRemoteDevice.startStreaming(request.getStreamIds(),
+                    request.getSurfaceIds());
+            request.recoverStreamIdToSurface();
+            List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+            requestList.add(request);
+
+            if (callback != null) {
+                mCaptureCallbackMap.put(requestInfo.getRequestId(),
+                        new CaptureCallbackHolder(
+                            callback, requestList, executor, true, mNextSessionId - 1));
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
+                }
+            }
+
+            if (mRepeatingRequestId != REQUEST_ID_NONE) {
+                checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId,
+                        requestInfo.getLastFrameNumber(), mRepeatingRequestTypes);
+            }
+
+            CaptureRequest[] requestArray = requestList.toArray(
+                    new CaptureRequest[requestList.size()]);
+            mRepeatingRequestId = requestInfo.getRequestId();
+            mRepeatingRequestTypes = getRequestTypes(requestArray);
+
+            if (mIdle) {
+                mDeviceExecutor.execute(mCallOnActive);
+            }
+            mIdle = false;
+
+            return requestInfo.getRequestId();
+        }
+    }
+
     public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
             Executor executor) throws CameraAccessException {
         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
@@ -2894,6 +2981,11 @@
     @Override
     public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
             throws CameraAccessException {
+        if (Flags.cameraMultiClient() && mSharedMode) {
+            throw new UnsupportedOperationException("In shared session mode,"
+                    + "extension sessions are not supported.");
+        }
+
         HashMap<String, CameraCharacteristics> characteristicsMap = new HashMap<>(
                 getPhysicalIdToChars());
         characteristicsMap.put(mCameraId, mCharacteristics);
@@ -2929,4 +3021,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
index a1f31c0..8c0dcfb 100644
--- a/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
@@ -19,6 +19,8 @@
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraOfflineSession;
+import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
 import android.hardware.camera2.CameraSharedCaptureSession;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.params.OutputConfiguration;
@@ -28,6 +30,7 @@
 
 import com.android.internal.camera.flags.Flags;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -46,7 +49,8 @@
     private static final String TAG = "CameraSharedCaptureSessionImpl";
     private final CameraCaptureSessionImpl mSessionImpl;
     private final ConditionVariable mInitialized = new ConditionVariable();
-    private boolean mIsPrimary;
+    private final android.hardware.camera2.impl.CameraDeviceImpl mCameraDevice;
+    private final Executor mDeviceExecutor;  
 
     /**
      * Create a new CameraCaptureSession.
@@ -54,24 +58,32 @@
     CameraSharedCaptureSessionImpl(int id,
             CameraCaptureSession.StateCallback callback, Executor stateExecutor,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
-            Executor deviceStateExecutor, boolean configureSuccess, boolean isPrimary) {
+            Executor deviceStateExecutor, boolean configureSuccess) {
         CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
         mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
                 stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
-        mIsPrimary = isPrimary;
+        mCameraDevice = deviceImpl;
+        mDeviceExecutor = deviceStateExecutor;
         mInitialized.open();
     }
 
     @Override
-    public int startStreaming(List<Surface> surfaces, Executor executor, CaptureCallback listener)
+    public int startStreaming(List<Surface> surfaces, Executor executor, CaptureCallback callback)
             throws CameraAccessException {
-        // Todo: Need to add implementation.
-        return 0;
+        if (surfaces.isEmpty()) {
+            throw new IllegalArgumentException("No surfaces provided for streaming");
+        } else if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        return mSessionImpl.startStreaming(surfaces, executor, callback);
     }
 
     @Override
     public void stopStreaming() throws CameraAccessException {
-      // Todo: Need to add implementation.
+        mSessionImpl.stopRepeating();
     }
 
     @Override
@@ -90,16 +102,24 @@
     }
 
     @Override
+    public boolean supportsOfflineProcessing(Surface surface) {
+        return false;
+    }
+
+    @Override
     public void abortCaptures() throws CameraAccessException {
-        if (mIsPrimary) {
+        if (mCameraDevice.isPrimaryClient()) {
             mSessionImpl.abortCaptures();
+            return;
         }
+        throw new UnsupportedOperationException("Shared capture session only supports this method"
+                + " for primary clients");
     }
 
     @Override
     public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
-        if (mIsPrimary) {
+        if (mCameraDevice.isPrimaryClient()) {
             return mSessionImpl.setRepeatingRequest(request, listener, handler);
         }
         throw new UnsupportedOperationException("Shared capture session only supports this method"
@@ -107,16 +127,30 @@
     }
 
     @Override
-    public void stopRepeating() throws CameraAccessException {
-        if (mIsPrimary) {
-            mSessionImpl.stopRepeating();
+    public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+            CaptureCallback listener)
+            throws CameraAccessException {
+        if (mCameraDevice.isPrimaryClient()) {
+            return mSessionImpl.setSingleRepeatingRequest(request, executor, listener);
         }
+        throw new UnsupportedOperationException("Shared capture session only supports this method"
+                + " for primary clients");
+    }
+
+    @Override
+    public void stopRepeating() throws CameraAccessException {
+        if (mCameraDevice.isPrimaryClient()) {
+            mSessionImpl.stopRepeating();
+            return;
+        }
+        throw new UnsupportedOperationException("Shared capture session only supports this method"
+                + " for primary clients");
     }
 
     @Override
     public int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
             throws CameraAccessException {
-        if (mIsPrimary) {
+        if (mCameraDevice.isPrimaryClient()) {
             return mSessionImpl.capture(request, listener, handler);
         }
         throw new UnsupportedOperationException("Shared capture session only supports this method"
@@ -124,6 +158,17 @@
     }
 
     @Override
+    public int captureSingleRequest(CaptureRequest request, Executor executor,
+            CaptureCallback listener)
+            throws CameraAccessException {
+        if (mCameraDevice.isPrimaryClient()) {
+            return mSessionImpl.captureSingleRequest(request, executor, listener);
+        }
+        throw new UnsupportedOperationException("Shared capture session only supports this method"
+                + " for primary clients");
+    }
+
+    @Override
     public void tearDown(Surface surface) throws CameraAccessException {
         mSessionImpl.tearDown(surface);
     }
@@ -149,48 +194,72 @@
     }
 
     @Override
+    public CameraOfflineSession switchToOffline(Collection<Surface> offlineSurfaces,
+            Executor executor, CameraOfflineSessionCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Shared capture session do not support this method"
+                );
+    }
+
+    @Override
     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
-        throw new UnsupportedOperationException("Shared Capture session doesn't support"
+        throw new UnsupportedOperationException("Shared Capture session do not support"
+                + " this method");
+    }
+
+    @Override
+    public int setRepeatingBurstRequests(List<CaptureRequest> requests,
+            Executor executor, CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Shared Capture session do not support"
                 + " this method");
     }
 
     @Override
     public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
-        throw new UnsupportedOperationException("Shared Capture session doesn't support"
+        throw new UnsupportedOperationException("Shared Capture session do not support"
+                + " this method");
+    }
+
+    @Override
+    public int captureBurstRequests(List<CaptureRequest> requests,
+            Executor executor, CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Shared Capture session do not support"
                 + " this method");
     }
 
     @Override
     public void updateOutputConfiguration(OutputConfiguration config)
             throws CameraAccessException {
-        throw new UnsupportedOperationException("Shared capture session doesn't support"
+        throw new UnsupportedOperationException("Shared capture session do not support"
                 + " this method");
     }
 
     @Override
     public void finalizeOutputConfigurations(List<OutputConfiguration> deferredOutputConfigs)
             throws CameraAccessException {
-        throw new UnsupportedOperationException("Shared capture session doesn't support"
+        throw new UnsupportedOperationException("Shared capture session do not support"
                 + " this method");
     }
 
     @Override
     public void prepare(Surface surface) throws CameraAccessException {
-        throw new UnsupportedOperationException("Shared capture session doesn't support"
+        throw new UnsupportedOperationException("Shared capture session do not support"
                 + " this method");
     }
 
     @Override
     public void prepare(int maxCount, Surface surface) throws CameraAccessException {
-        throw new UnsupportedOperationException("Shared capture session doesn't support"
+        throw new UnsupportedOperationException("Shared capture session do not support"
                 + " this method");
     }
 
     @Override
     public void closeWithoutDraining() {
-        throw new UnsupportedOperationException("Shared capture session doesn't support"
+        throw new UnsupportedOperationException("Shared capture session do not support"
                 + " this method");
     }
 
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index a79e084..0b8e9c2 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -65,6 +65,17 @@
         }
     }
 
+    public SubmitInfo startStreaming(int[] streamIdxArray, int[] surfaceIdxArray)
+            throws CameraAccessException {
+        try {
+            return mRemoteDevice.startStreaming(streamIdxArray, surfaceIdxArray);
+        } catch (ServiceSpecificException e) {
+            throw ExceptionUtils.throwAsPublicException(e);
+        } catch (RemoteException e) {
+            throw ExceptionUtils.throwAsPublicException(e);
+        }
+    }
+
     public SubmitInfo submitRequest(CaptureRequest request, boolean streaming)
             throws CameraAccessException  {
         try {
@@ -325,4 +336,4 @@
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index e12c463..d394154 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1803,6 +1803,19 @@
     }
 
     /**
+     * Get the flag indicating if this {@link OutputConfiguration} is for a multi-resolution output
+     * with a MultiResolutionImageReader.
+     *
+     * @return true if this {@link OutputConfiguration} is for a multi-resolution output with a
+     *              MultiResolutionImageReader.
+     *
+     * @hide
+     */
+    public boolean isMultiResolution() {
+        return mIsMultiResolution;
+    }
+
+    /**
      * Get the physical camera ID associated with this {@link OutputConfiguration}.
      *
      * <p>If this OutputConfiguration isn't targeting a physical camera of a logical
diff --git a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
index cdcc92c..365f870 100644
--- a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
@@ -212,7 +212,7 @@
         }
 
         public @Nullable String getPhysicalCameraId() {
-            return mPhysicalCameraId;
+            return mPhysicalCameraId.isEmpty() ? null : mPhysicalCameraId;
         }
     }
 
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 1e5bed5..de88895 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -539,7 +539,10 @@
             return this;
         }
 
-        /** Attach a callback interface for lifecycle events for this Endpoint */
+        /**
+         * Attach a callback interface for lifecycle events for this Endpoint. Callback will be
+         * posted to the main thread.
+         */
         @NonNull
         public Builder setLifecycleCallback(
                 @NonNull HubEndpointLifecycleCallback lifecycleCallback) {
@@ -560,7 +563,10 @@
             return this;
         }
 
-        /** Attach a callback interface for message events for this Endpoint */
+        /**
+         * Attach a callback interface for message events for this Endpoint. Callback will be posted
+         * to the main thread.
+         */
         @NonNull
         public Builder setMessageCallback(@NonNull HubEndpointMessageCallback messageCallback) {
             mMessageCallback = messageCallback;
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 313bad5..c4d11cd 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -204,3 +204,10 @@
     description: "Allows the user to disable input scrolling acceleration for mouse."
     bug: "383555305"
 }
+
+flag {
+  name: "remove_fallback_modifiers"
+  namespace: "input"
+  description: "Removes modifiers from the original key event that activated the fallback, ensuring that only the intended fallback event is sent."
+  bug: "382545048"
+}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
index 90136ae..ffe8086 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
@@ -93,6 +93,10 @@
  * must do its own state management (keeping in mind that the service's process might be killed
  * by the Android System when unbound; for example, if the device is running low in memory).
  *
+ * <p> The service also provides pending intents to override the system's Quick Access activities
+ * via the {@link #getTargetActivityPendingIntent} and the
+ * {@link #getGestureTargetActivityPendingIntent} method.
+ *
  * <p>
  * <a name="ErrorHandling"></a>
  * <h3>Error handling</h3>
@@ -384,6 +388,10 @@
      *
      * <p>The pending intent will be sent when the user performs a gesture to open Wallet.
      * The pending intent should launch an activity.
+     *
+     * <p> If the gesture is performed and this method returns null, the system will launch the
+     * activity specified by the {@link #getTargetActivityPendingIntent} method. If that method
+     * also returns null, the system will launch the system-provided card switcher activity.
      */
     @Nullable
     @FlaggedApi(Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index fb1bd17..6116d59 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -70,3 +70,11 @@
     is_fixed_read_only: true
     bug: "352538294"
 }
+
+flag {
+    name: "system_server_large_perfetto_shmem_buffer"
+    namespace: "windowing_tools"
+    description: "Large perfetto shmem buffer"
+    is_fixed_read_only: true
+    bug: "382369925"
+}
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index a8d4e2d..48dfdd4 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,9 @@
 
 package android.view;
 
+
+import static com.android.hardware.input.Flags.removeFallbackModifiers;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -458,7 +461,15 @@
         FallbackAction action = FallbackAction.obtain();
         metaState = KeyEvent.normalizeMetaState(metaState);
         if (nativeGetFallbackAction(mPtr, keyCode, metaState, action)) {
-            action.metaState = KeyEvent.normalizeMetaState(action.metaState);
+            if (removeFallbackModifiers()) {
+                // Strip all modifiers. This is safe to do since only exact keyCode + metaState
+                // modifiers will trigger a fallback.
+                // E.g. Ctrl + Space -> language_switch (fallback generated)
+                //      Ctrl + Alt + Space -> Ctrl + Alt + Space (no fallback generated)
+                action.metaState = 0;
+            } else {
+                action.metaState = KeyEvent.normalizeMetaState(action.metaState);
+            }
             return action;
         }
         action.recycle();
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index c97d3ec..abd93cf 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -105,6 +105,13 @@
 
 flag {
     namespace: "windowing_sdk"
+    name: "activity_embedding_support_for_connected_displays"
+    description: "Enables activity embedding support for connected displays, including enabling AE optimization for Settings."
+    bug: "369438353"
+}
+
+flag {
+    namespace: "windowing_sdk"
     name: "wlinfo_oncreate"
     description: "Makes WindowLayoutInfo accessible without racing in the Activity#onCreate()"
     bug: "337820752"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 76c810b..e91e111 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -157,39 +157,27 @@
             android:maxDrawableHeight="@dimen/notification_right_icon_size"
             />
 
-        <LinearLayout
-            android:id="@+id/notification_buttons_column"
+        <FrameLayout
+            android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:layout_alignParentEnd="true"
-            android:orientation="vertical"
+            android:minWidth="@dimen/notification_content_margin_end"
             >
 
-            <include layout="@layout/notification_close_button"
-                android:layout_width="@dimen/notification_close_button_size"
-                android:layout_height="@dimen/notification_close_button_size"
-                android:layout_gravity="end"
-                android:layout_marginEnd="20dp"
+            <include layout="@layout/notification_expand_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|end"
                 />
 
-            <FrameLayout
-                android:id="@+id/expand_button_touch_container"
-                android:layout_width="wrap_content"
-                android:layout_height="0dp"
-                android:layout_weight="1"
-                android:minWidth="@dimen/notification_content_margin_end"
-                >
-
-                <include layout="@layout/notification_expand_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical|end"
-                    />
-
-            </FrameLayout>
-
-        </LinearLayout>
+        </FrameLayout>
 
     </LinearLayout>
 
+    <include layout="@layout/notification_close_button"
+        android:id="@+id/close_button"
+        android:layout_width="@dimen/notification_close_button_size"
+        android:layout_height="@dimen/notification_close_button_size"
+        android:layout_gravity="top|end" />
+
 </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 2e0a7af..2d36733 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -194,4 +194,11 @@
         </FrameLayout>
 
     </LinearLayout>
+
+    <include layout="@layout/notification_close_button"
+        android:id="@+id/close_button"
+        android:layout_width="@dimen/notification_close_button_size"
+        android:layout_height="@dimen/notification_close_button_size"
+        android:layout_gravity="top|end" />
+
 </com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index f644ade..fbecb8c 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -199,6 +199,12 @@
 
             </LinearLayout>
 
+            <include layout="@layout/notification_close_button"
+                android:id="@+id/close_button"
+                android:layout_width="@dimen/notification_close_button_size"
+                android:layout_height="@dimen/notification_close_button_size"
+                android:layout_gravity="top|end" />
+
         </com.android.internal.widget.NotificationMaxHeightFrameLayout>
 
     <LinearLayout
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
index fc727e1..2d30d8a 100644
--- a/core/res/res/layout/notification_2025_template_header.xml
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -60,7 +60,7 @@
         android:layout_height="match_parent"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
-        android:layout_toStartOf="@id/notification_buttons_column"
+        android:layout_toStartOf="@id/expand_button"
         android:layout_alignWithParentIfMissing="true"
         android:clipChildren="false"
         android:gravity="center_vertical"
@@ -81,28 +81,17 @@
         android:focusable="false"
         />
 
-    <LinearLayout
-        android:id="@+id/notification_buttons_column"
+    <include layout="@layout/notification_expand_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentEnd="true"
-        android:orientation="vertical"
-        >
+        android:layout_centerVertical="true"
+        android:layout_alignParentEnd="true" />
 
-        <include layout="@layout/notification_close_button"
-            android:layout_width="@dimen/notification_close_button_size"
-            android:layout_height="@dimen/notification_close_button_size"
-            android:layout_gravity="end"
-            android:layout_marginEnd="20dp"
-            />
-
-        <include layout="@layout/notification_expand_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentEnd="true"
-            android:layout_centerVertical="true"
-            />
-
-    </LinearLayout>
+    <include layout="@layout/notification_close_button"
+        android:id="@+id/close_button"
+        android:layout_width="@dimen/notification_close_button_size"
+        android:layout_height="@dimen/notification_close_button_size"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentEnd="true" />
 
 </NotificationHeaderView>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 565e28e..45a5d85 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2165,6 +2165,17 @@
          config_enableGeofenceOverlay is false. -->
     <string name="config_geofenceProviderPackageName" translatable="false">@null</string>
 
+    <!-- Whether to enable GNSS assistance overlay which allows GnssAssistanceProvider to be
+     replaced by an app at run-time. When disabled, only the
+     config_gnssAssistanceProviderPackageName package will be searched for
+     GnssAssistanceProvider, otherwise any system package is eligible. Anyone who wants to
+     disable the overlay mechanism can set it to false.
+     -->
+    <bool name="config_enableGnssAssistanceOverlay" translatable="false">true</bool>
+    <!-- Package name providing GNSS assistance API support. Used only when
+         config_enableGnssAssistanceOverlay is false. -->
+    <string name="config_gnssAssistanceProviderPackageName" translatable="false">@null</string>
+
     <!-- Whether to enable Hardware Activity-Recognition overlay which allows Hardware
          Activity-Recognition to be replaced by an app at run-time. When disabled, only the
          config_activityRecognitionHardwarePackageName package will be searched for
diff --git a/core/res/res/values/config_watch.xml b/core/res/res/values/config_watch.xml
index 629a343..bcb1e09 100644
--- a/core/res/res/values/config_watch.xml
+++ b/core/res/res/values/config_watch.xml
@@ -15,8 +15,7 @@
   -->
 
 <resources>
-    <!--  TODO(b/382103556): use predefined Material3 token  -->
     <!-- For Wear Material3 -->
-    <dimen name="config_wearMaterial3_buttonCornerRadius">26dp</dimen>
-    <dimen name="config_wearMaterial3_bottomDialogCornerRadius">18dp</dimen>
+    <dimen name="config_wearMaterial3_buttonCornerRadius">@dimen/config_shapeCornerRadiusLarge</dimen>
+    <dimen name="config_wearMaterial3_bottomDialogCornerRadius">@dimen/config_shapeCornerRadiusMedium</dimen>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 73e06f6..8195d38 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2040,6 +2040,7 @@
   <java-symbol type="bool" name="config_useGnssHardwareProvider" />
   <java-symbol type="bool" name="config_enableGeocoderOverlay" />
   <java-symbol type="bool" name="config_enableGeofenceOverlay" />
+  <java-symbol type="bool" name="config_enableGnssAssistanceOverlay" />
   <java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
   <java-symbol type="bool" name="config_sf_limitedAlpha" />
   <java-symbol type="bool" name="config_unplugTurnsOnScreen" />
@@ -2223,6 +2224,7 @@
   <java-symbol type="string" name="config_gnssLocationProviderPackageName" />
   <java-symbol type="string" name="config_geocoderProviderPackageName" />
   <java-symbol type="string" name="config_geofenceProviderPackageName" />
+  <java-symbol type="string" name="config_gnssAssistanceProviderPackageName" />
   <java-symbol type="string" name="config_networkLocationProviderPackageName" />
   <java-symbol type="string" name="config_wimaxManagerClassname" />
   <java-symbol type="string" name="config_wimaxNativeLibLocation" />
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index 41d1b5c..eecf199 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -55,6 +55,7 @@
         "truth",
         "flag-junit-base",
         "flag-junit",
+        "testables",
     ],
     auto_gen_config: true,
 }
@@ -77,6 +78,7 @@
         "truth",
         "platform-test-annotations",
         "platform-test-rules",
+        "testables",
     ],
     libs: [
         "android.test.base.stubs.system",
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
index 0d8f809..3e01256 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.bubbles.bar
 
+import android.animation.AnimatorTestRule
+import android.app.ActivityManager
 import android.content.Context
 import android.graphics.Insets
 import android.graphics.Rect
@@ -23,7 +25,6 @@
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.widget.FrameLayout
-import androidx.core.animation.AnimatorTestRule
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -36,27 +37,34 @@
 import com.android.wm.shell.bubbles.BubbleLogger
 import com.android.wm.shell.bubbles.BubbleOverflow
 import com.android.wm.shell.bubbles.BubblePositioner
+import com.android.wm.shell.bubbles.BubbleTaskView
 import com.android.wm.shell.bubbles.DeviceConfig
 import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
 import com.android.wm.shell.bubbles.FakeBubbleFactory
-import com.android.wm.shell.bubbles.FakeBubbleTaskViewFactory
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Semaphore
 import java.util.concurrent.TimeUnit
 import org.junit.After
 import org.junit.Before
-import org.junit.ClassRule
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 /** Tests for [BubbleBarAnimationHelper] */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class BubbleBarAnimationHelperTest {
 
-    companion object {
-        @JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+    @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
 
+    companion object {
         const val SCREEN_WIDTH = 2000
         const val SCREEN_HEIGHT = 1000
     }
@@ -148,6 +156,26 @@
     }
 
     @Test
+    fun animateSwitch_bubbleToBubble_updateTaskBounds() {
+        val fromBubble = createBubble("from").initialize(container)
+        val toBubbleTaskController = mock<TaskViewTaskController>()
+        val toBubble = createBubble("to", toBubbleTaskController).initialize(container)
+
+        getInstrumentation().runOnMainSync {
+            animationHelper.animateSwitch(fromBubble, toBubble) {}
+            // Start the animation, but don't finish
+            animatorTestRule.advanceTimeBy(100)
+        }
+        getInstrumentation().waitForIdleSync()
+        // Clear invocations to ensure that bounds update happens after animation ends
+        clearInvocations(toBubbleTaskController)
+        getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(900) }
+        getInstrumentation().waitForIdleSync()
+
+        verify(toBubbleTaskController).setWindowBounds(any())
+    }
+
+    @Test
     fun animateSwitch_bubbleToOverflow_oldHiddenNewShown() {
         val fromBubble = createBubble(key = "from").initialize(container)
         val overflow = createOverflow().initialize(container)
@@ -193,13 +221,43 @@
         assertThat(toBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
     }
 
-    private fun createBubble(key: String): Bubble {
+    @Test
+    fun animateToRestPosition_updateTaskBounds() {
+        val taskController = mock<TaskViewTaskController>()
+        val bubble = createBubble("key", taskController).initialize(container)
+
+        getInstrumentation().runOnMainSync {
+            animationHelper.animateExpansion(bubble) {}
+            animatorTestRule.advanceTimeBy(1000)
+        }
+        getInstrumentation().waitForIdleSync()
+        getInstrumentation().runOnMainSync {
+            animationHelper.animateToRestPosition()
+            animatorTestRule.advanceTimeBy(100)
+        }
+        // Clear invocations to ensure that bounds update happens after animation ends
+        clearInvocations(taskController)
+        getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(900) }
+        getInstrumentation().waitForIdleSync()
+
+        verify(taskController).setWindowBounds(any())
+    }
+
+    private fun createBubble(
+        key: String,
+        taskViewTaskController: TaskViewTaskController = mock<TaskViewTaskController>(),
+    ): Bubble {
+        val taskView = TaskView(context, taskViewTaskController)
+        val taskInfo = mock<ActivityManager.RunningTaskInfo>()
+        whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+        val bubbleTaskView = BubbleTaskView(taskView, mainExecutor)
+
         val bubbleBarExpandedView =
             FakeBubbleFactory.createExpandedView(
                 context,
                 bubblePositioner,
                 expandedViewManager,
-                FakeBubbleTaskViewFactory(context, mainExecutor).create(),
+                bubbleTaskView,
                 mainExecutor,
                 bgExecutor,
                 bubbleLogger,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 3e8a9b6..3188e5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -463,6 +463,7 @@
                 super.onAnimationEnd(animation);
                 bbev.resetPivot();
                 bbev.setDragging(false);
+                updateExpandedView(bbev);
             }
         });
         startNewAnimator(animatorSet);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index c74bf53..9ebb7f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -643,7 +643,9 @@
                         t.setPosition(animatingLeash, x, endY);
                         t.setAlpha(animatingLeash, 1.f);
                     }
-                    dispatchEndPositioning(mDisplayId, mCancelled, t);
+                    if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+                        dispatchEndPositioning(mDisplayId, mCancelled, t);
+                    }
                     if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
                         ImeTracker.forLogging().onProgress(mStatsToken,
                                 ImeTracker.PHASE_WM_ANIMATION_RUNNING);
@@ -659,6 +661,14 @@
                         ImeTracker.forLogging().onCancelled(mStatsToken,
                                 ImeTracker.PHASE_WM_ANIMATION_RUNNING);
                     }
+                    if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                        // In split screen, we also set {@link
+                        // WindowContainer#mExcludeInsetsTypes} but this should only happen after
+                        // the IME client visibility was set. Otherwise the insets will we
+                        // dispatched too early, and we get a flicker. Thus, only dispatching it
+                        // after reporting that the IME is hidden to system server.
+                        dispatchEndPositioning(mDisplayId, mCancelled, t);
+                    }
                     if (DEBUG_IME_VISIBILITY) {
                         EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
                                 mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 1bc15d7..cc4a29b 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -199,6 +199,7 @@
         // This is to suppress warnings/errors from gtest
         "-Wno-unnamed-type-template-args",
     ],
+    require_root: true,
     srcs: [
         // Helpers/infra for testing.
         "tests/CommonHelpers.cpp",
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
index 70d14a1..9704634 100644
--- a/libs/androidfw/TypeWrappers.cpp
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -16,8 +16,6 @@
 
 #include <androidfw/TypeWrappers.h>
 
-#include <algorithm>
-
 namespace android {
 
 TypeVariant::TypeVariant(const ResTable_type* data) : data(data), mLength(dtohl(data->entryCount)) {
@@ -31,30 +29,44 @@
             ALOGE("Type's entry indices extend beyond its boundaries");
             mLength = 0;
         } else {
-          mLength = ResTable_sparseTypeEntry{entryIndices[entryCount - 1]}.idx + 1;
+          mLength = dtohs(ResTable_sparseTypeEntry{entryIndices[entryCount - 1]}.idx) + 1;
         }
     }
 }
 
 TypeVariant::iterator& TypeVariant::iterator::operator++() {
-    mIndex++;
+    ++mIndex;
     if (mIndex > mTypeVariant->mLength) {
         mIndex = mTypeVariant->mLength;
     }
+
+    const ResTable_type* type = mTypeVariant->data;
+    if ((type->flags & ResTable_type::FLAG_SPARSE) == 0) {
+      return *this;
+    }
+
+    // Need to adjust |mSparseIndex| as well if we've passed its current element.
+    const uint32_t entryCount = dtohl(type->entryCount);
+    const auto entryIndices = reinterpret_cast<const uint32_t*>(
+        reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
+    if (mSparseIndex >= entryCount) {
+      return *this; // done
+    }
+    const auto element = (const ResTable_sparseTypeEntry*)(entryIndices + mSparseIndex);
+    if (mIndex > dtohs(element->idx)) {
+      ++mSparseIndex;
+    }
+
     return *this;
 }
 
-static bool keyCompare(uint32_t entry, uint16_t index) {
-  return dtohs(ResTable_sparseTypeEntry{entry}.idx) < index;
-}
-
 const ResTable_entry* TypeVariant::iterator::operator*() const {
-    const ResTable_type* type = mTypeVariant->data;
     if (mIndex >= mTypeVariant->mLength) {
-        return NULL;
+        return nullptr;
     }
 
-    const uint32_t entryCount = dtohl(mTypeVariant->data->entryCount);
+    const ResTable_type* type = mTypeVariant->data;
+    const uint32_t entryCount = dtohl(type->entryCount);
     const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type)
             + dtohl(type->header.size);
     const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
@@ -63,18 +75,19 @@
                                     sizeof(uint16_t) : sizeof(uint32_t);
     if (reinterpret_cast<uintptr_t>(entryIndices) + (indexSize * entryCount) > containerEnd) {
         ALOGE("Type's entry indices extend beyond its boundaries");
-        return NULL;
+        return nullptr;
     }
 
     uint32_t entryOffset;
     if (type->flags & ResTable_type::FLAG_SPARSE) {
-      auto iter = std::lower_bound(entryIndices, entryIndices + entryCount, mIndex, keyCompare);
-      if (iter == entryIndices + entryCount
-              || dtohs(ResTable_sparseTypeEntry{*iter}.idx) != mIndex) {
-        return NULL;
+      if (mSparseIndex >= entryCount) {
+        return nullptr;
       }
-
-      entryOffset = static_cast<uint32_t>(dtohs(ResTable_sparseTypeEntry{*iter}.offset)) * 4u;
+      const auto element = (const ResTable_sparseTypeEntry*)(entryIndices + mSparseIndex);
+      if (dtohs(element->idx) != mIndex) {
+        return nullptr;
+      }
+      entryOffset = static_cast<uint32_t>(dtohs(element->offset)) * 4u;
     } else if (type->flags & ResTable_type::FLAG_OFFSET16) {
       auto entryIndices16 = reinterpret_cast<const uint16_t*>(entryIndices);
       entryOffset = offset_from16(entryIndices16[mIndex]);
@@ -83,25 +96,25 @@
     }
 
     if (entryOffset == ResTable_type::NO_ENTRY) {
-        return NULL;
+        return nullptr;
     }
 
     if ((entryOffset & 0x3) != 0) {
         ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset);
-        return NULL;
+        return nullptr;
     }
 
     const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
             reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset);
     if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
         ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
-        return NULL;
+        return nullptr;
     } else if (reinterpret_cast<uintptr_t>(entry) + entry->size() > containerEnd) {
         ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
-        return NULL;
+        return nullptr;
     } else if (entry->size() < sizeof(*entry)) {
         ALOGE("Entry at index %u is too small (%zu)", mIndex, entry->size());
-        return NULL;
+        return nullptr;
     }
     return entry;
 }
diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
index fb2fad6..db641b7 100644
--- a/libs/androidfw/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -27,24 +27,14 @@
 
     class iterator {
     public:
-        iterator& operator=(const iterator& rhs) {
-            mTypeVariant = rhs.mTypeVariant;
-            mIndex = rhs.mIndex;
-            return *this;
-        }
-
         bool operator==(const iterator& rhs) const {
             return mTypeVariant == rhs.mTypeVariant && mIndex == rhs.mIndex;
         }
 
-        bool operator!=(const iterator& rhs) const {
-            return mTypeVariant != rhs.mTypeVariant || mIndex != rhs.mIndex;
-        }
-
         iterator operator++(int) {
-            uint32_t prevIndex = mIndex;
+            iterator prev = *this;
             operator++();
-            return iterator(mTypeVariant, prevIndex);
+            return prev;
         }
 
         const ResTable_entry* operator->() const {
@@ -60,18 +50,26 @@
 
     private:
         friend struct TypeVariant;
-        iterator(const TypeVariant* tv, uint32_t index)
-            : mTypeVariant(tv), mIndex(index) {}
+
+        enum class Kind { Begin, End };
+        iterator(const TypeVariant* tv, Kind kind)
+            : mTypeVariant(tv) {
+          mSparseIndex = mIndex = kind == Kind::Begin ? 0 : tv->mLength;
+          // mSparseIndex here is technically past the number of sparse entries, but it is still
+          // ok as it is enough to infer that this is the end iterator.
+        }
+
         const TypeVariant* mTypeVariant;
         uint32_t mIndex;
+        uint32_t mSparseIndex;
     };
 
     iterator beginEntries() const {
-        return iterator(this, 0);
+        return iterator(this, iterator::Kind::Begin);
     }
 
     iterator endEntries() const {
-        return iterator(this, mLength);
+        return iterator(this, iterator::Kind::End);
     }
 
     const ResTable_type* data;
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
index ed30904..d66e058 100644
--- a/libs/androidfw/tests/TypeWrappers_test.cpp
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -14,28 +14,42 @@
  * limitations under the License.
  */
 
-#include <algorithm>
 #include <androidfw/ResourceTypes.h>
 #include <androidfw/TypeWrappers.h>
-#include <utils/String8.h>
+#include <androidfw/Util.h>
+
+#include <optional>
+#include <vector>
 
 #include <gtest/gtest.h>
 
 namespace android {
 
-// create a ResTable_type in memory with a vector of Res_value*
-static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
-                             bool compact_entry = false,
-                             bool short_offsets = false)
+using ResValueVector = std::vector<std::optional<Res_value>>;
+
+// create a ResTable_type in memory
+static util::unique_cptr<ResTable_type> createTypeTable(
+    const ResValueVector& in_values, bool compact_entry, bool short_offsets, bool sparse)
 {
+    ResValueVector sparse_values;
+    if (sparse) {
+      std::ranges::copy_if(in_values, std::back_inserter(sparse_values),
+                           [](auto&& v) { return v.has_value(); });
+    }
+    const ResValueVector& values = sparse ? sparse_values : in_values;
+
     ResTable_type t{};
     t.header.type = RES_TABLE_TYPE_TYPE;
     t.header.headerSize = sizeof(t);
     t.header.size = sizeof(t);
     t.id = 1;
-    t.flags = short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
+    t.flags = sparse
+                  ? ResTable_type::FLAG_SPARSE
+                  : short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
 
-    t.header.size += values.size() * (short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
+    t.header.size += values.size() *
+                     (sparse ? sizeof(ResTable_sparseTypeEntry) :
+                         short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
     t.entriesStart = t.header.size;
     t.entryCount = values.size();
 
@@ -53,9 +67,18 @@
     memcpy(p_header, &t, sizeof(t));
 
     size_t i = 0, entry_offset = 0;
-    uint32_t k = 0;
-    for (auto const& v : values) {
-        if (short_offsets) {
+    uint32_t sparse_index = 0;
+
+    for (auto const& v : in_values) {
+        if (sparse) {
+            if (!v) {
+                ++i;
+                continue;
+            }
+            const auto p = reinterpret_cast<ResTable_sparseTypeEntry*>(p_offsets) + sparse_index++;
+            p->idx = i;
+            p->offset = (entry_offset >> 2) & 0xffffu;
+        } else if (short_offsets) {
             uint16_t *p = reinterpret_cast<uint16_t *>(p_offsets) + i;
             *p = v ? (entry_offset >> 2) & 0xffffu : 0xffffu;
         } else {
@@ -83,62 +106,92 @@
         }
         i++;
     }
-    return reinterpret_cast<ResTable_type*>(data);
+    return util::unique_cptr<ResTable_type>{reinterpret_cast<ResTable_type*>(data)};
 }
 
 TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
-    std::vector<Res_value *> values;
+    ResValueVector values;
 
-    Res_value *v1 = new Res_value{};
-    values.push_back(v1);
-
-    values.push_back(nullptr);
-
-    Res_value *v2 = new Res_value{};
-    values.push_back(v2);
-
-    Res_value *v3 = new Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678};
-    values.push_back(v3);
+    values.push_back(std::nullopt);
+    values.push_back(Res_value{});
+    values.push_back(std::nullopt);
+    values.push_back(Res_value{});
+    values.push_back(Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678});
+    values.push_back(std::nullopt);
+    values.push_back(std::nullopt);
+    values.push_back(std::nullopt);
+    values.push_back(Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x87654321});
 
     // test for combinations of compact_entry and short_offsets
-    for (size_t i = 0; i < 4; i++) {
-        bool compact_entry = i & 0x1, short_offsets = i & 0x2;
-        ResTable_type* data = createTypeTable(values, compact_entry, short_offsets);
-        TypeVariant v(data);
+    for (size_t i = 0; i < 8; i++) {
+        bool compact_entry = i & 0x1, short_offsets = i & 0x2, sparse = i & 0x4;
+        auto data = createTypeTable(values, compact_entry, short_offsets, sparse);
+        TypeVariant v(data.get());
 
         TypeVariant::iterator iter = v.beginEntries();
         ASSERT_EQ(uint32_t(0), iter.index());
-        ASSERT_TRUE(NULL != *iter);
-        ASSERT_EQ(uint32_t(0), iter->key());
+        ASSERT_TRUE(NULL == *iter);
         ASSERT_NE(v.endEntries(), iter);
 
-        iter++;
+        ++iter;
 
         ASSERT_EQ(uint32_t(1), iter.index());
-        ASSERT_TRUE(NULL == *iter);
+        ASSERT_TRUE(NULL != *iter);
+        ASSERT_EQ(uint32_t(1), iter->key());
         ASSERT_NE(v.endEntries(), iter);
 
         iter++;
 
         ASSERT_EQ(uint32_t(2), iter.index());
+        ASSERT_TRUE(NULL == *iter);
+        ASSERT_NE(v.endEntries(), iter);
+
+        ++iter;
+
+        ASSERT_EQ(uint32_t(3), iter.index());
         ASSERT_TRUE(NULL != *iter);
-        ASSERT_EQ(uint32_t(2), iter->key());
+        ASSERT_EQ(uint32_t(3), iter->key());
         ASSERT_NE(v.endEntries(), iter);
 
         iter++;
 
-        ASSERT_EQ(uint32_t(3), iter.index());
+        ASSERT_EQ(uint32_t(4), iter.index());
         ASSERT_TRUE(NULL != *iter);
         ASSERT_EQ(iter->is_compact(), compact_entry);
-        ASSERT_EQ(uint32_t(3), iter->key());
+        ASSERT_EQ(uint32_t(4), iter->key());
         ASSERT_EQ(uint32_t(0x12345678), iter->value().data);
         ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
 
+        ++iter;
+
+        ASSERT_EQ(uint32_t(5), iter.index());
+        ASSERT_TRUE(NULL == *iter);
+        ASSERT_NE(v.endEntries(), iter);
+
+        ++iter;
+
+        ASSERT_EQ(uint32_t(6), iter.index());
+        ASSERT_TRUE(NULL == *iter);
+        ASSERT_NE(v.endEntries(), iter);
+
+        ++iter;
+
+        ASSERT_EQ(uint32_t(7), iter.index());
+        ASSERT_TRUE(NULL == *iter);
+        ASSERT_NE(v.endEntries(), iter);
+
         iter++;
 
-        ASSERT_EQ(v.endEntries(), iter);
+        ASSERT_EQ(uint32_t(8), iter.index());
+        ASSERT_TRUE(NULL != *iter);
+        ASSERT_EQ(iter->is_compact(), compact_entry);
+        ASSERT_EQ(uint32_t(8), iter->key());
+        ASSERT_EQ(uint32_t(0x87654321), iter->value().data);
+        ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
 
-        free(data);
+        ++iter;
+
+        ASSERT_EQ(v.endEntries(), iter);
     }
 }
 
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index 8af4b7e..4fecf4d 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -59,7 +59,6 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.os.statsd",
-        "test_com.android.os.statsd",
         "com.android.uprobestats",
     ],
 }
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index 0c2f3ad..023bad2 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -1459,6 +1459,13 @@
     field public static final String ACTION_GEOCODE_PROVIDER = "com.android.location.service.GeocodeProvider";
   }
 
+  @FlaggedApi("android.location.flags.gnss_assistance_interface") public abstract class GnssAssistanceProviderBase {
+    ctor public GnssAssistanceProviderBase(@NonNull android.content.Context, @NonNull String);
+    method @NonNull public final android.os.IBinder getBinder();
+    method public abstract void onRequest(@NonNull android.os.OutcomeReceiver<android.location.GnssAssistance,java.lang.Throwable>);
+    field public static final String ACTION_GNSS_ASSISTANCE_PROVIDER = "android.location.provider.action.GNSS_ASSISTANCE_PROVIDER";
+  }
+
   public abstract class LocationProviderBase {
     ctor public LocationProviderBase(@NonNull android.content.Context, @NonNull String, @NonNull android.location.provider.ProviderProperties);
     method @Nullable public final android.os.IBinder getBinder();
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index c02cc80..1b38982 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -167,4 +167,4 @@
     namespace: "location"
     description: "Flag for GNSS assistance interface"
     bug: "209078566"
-}
\ No newline at end of file
+}
diff --git a/location/java/android/location/provider/GnssAssistanceProviderBase.java b/location/java/android/location/provider/GnssAssistanceProviderBase.java
new file mode 100644
index 0000000..f4b26d5
--- /dev/null
+++ b/location/java/android/location/provider/GnssAssistanceProviderBase.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.location.GnssAssistance;
+import android.location.flags.Flags;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+
+/**
+ * Base class for GNSS assistance providers outside the system server.
+ *
+ * <p>GNSS assistance providers should be wrapped in a non-exported service which returns the result
+ * of {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The
+ * service should not be exported so that components other than the system server cannot bind to it.
+ * Alternatively, the service may be guarded by a permission that only system server can obtain. The
+ * service may specify metadata on its capabilities:
+ *
+ * <ul>
+ *   <li>"serviceVersion": An integer version code to help tie break if multiple services are
+ *       capable of implementing the geocode provider. All else equal, the service with the highest
+ *       version code will be chosen. Assumed to be 0 if not specified.
+ *   <li>"serviceIsMultiuser": A boolean property, indicating if the service wishes to take
+ *       responsibility for handling changes to the current user on the device. If true, the service
+ *       will always be bound from the system user. If false, the service will always be bound from
+ *       the current user. If the current user changes, the old binding will be released, and a new
+ *       binding established under the new user. Assumed to be false if not specified.
+ * </ul>
+ *
+ * <p>The service should have an intent filter in place for the GNSS assistance provider as
+ * specified by the constant in this class.
+ *
+ * <p>GNSS assistance providers are identified by their UID / package name / attribution tag. Based
+ * on this identity, geocode providers may be given some special privileges.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public abstract class GnssAssistanceProviderBase {
+
+    /**
+     * The action the wrapping service should have in its intent filter to implement the GNSS
+     * Assistance provider.
+     */
+    public static final String ACTION_GNSS_ASSISTANCE_PROVIDER =
+            "android.location.provider.action.GNSS_ASSISTANCE_PROVIDER";
+
+    final String mTag;
+    @Nullable
+    final String mAttributionTag;
+    final IBinder mBinder;
+
+    /**
+     * Subclasses should pass in a context and an arbitrary tag that may be used for logcat logging
+     * of errors, and thus should uniquely identify the class.
+     */
+    public GnssAssistanceProviderBase(@NonNull Context context, @NonNull String tag) {
+        mTag = tag;
+        mAttributionTag = context.getAttributionTag();
+        mBinder = new GnssAssistanceProviderBase.Service();
+    }
+
+    /**
+     * Returns the IBinder instance that should be returned from the {@link
+     * android.app.Service#onBind(Intent)} method of the wrapping service.
+     */
+    @NonNull
+    public final IBinder getBinder() {
+        return mBinder;
+    }
+
+    /**
+     * Requests GNSS assistance data of the given arguments. The given callback must be invoked
+     * once.
+     */
+    public abstract void onRequest(
+            @NonNull OutcomeReceiver<GnssAssistance, Throwable> callback);
+
+    private class Service extends IGnssAssistanceProvider.Stub {
+        @Override
+        public void request(IGnssAssistanceCallback callback) {
+            try {
+                onRequest(new GnssAssistanceProviderBase.SingleUseCallback(callback));
+            } catch (RuntimeException e) {
+                // exceptions on one-way binder threads are dropped - move to a different thread
+                Log.w(mTag, e);
+                new Handler(Looper.getMainLooper())
+                        .post(
+                                () -> {
+                                    throw new AssertionError(e);
+                                });
+            }
+        }
+    }
+
+    private static class SingleUseCallback implements
+            OutcomeReceiver<GnssAssistance, Throwable> {
+
+        private final AtomicReference<IGnssAssistanceCallback> mCallback;
+
+        SingleUseCallback(IGnssAssistanceCallback callback) {
+            mCallback = new AtomicReference<>(callback);
+        }
+
+        @Override
+        public void onError(Throwable e) {
+            try {
+                Objects.requireNonNull(mCallback.getAndSet(null)).onError();
+            } catch (RemoteException r) {
+                throw r.rethrowFromSystemServer();
+            }
+        }
+
+        @Override
+        public void onResult(GnssAssistance result) {
+            try {
+                Objects.requireNonNull(mCallback.getAndSet(null)).onResult(result);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+}
diff --git a/location/java/android/location/provider/IGnssAssistanceCallback.aidl b/location/java/android/location/provider/IGnssAssistanceCallback.aidl
new file mode 100644
index 0000000..ea38d08
--- /dev/null
+++ b/location/java/android/location/provider/IGnssAssistanceCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.GnssAssistance;
+
+/**
+ * Binder interface for GNSS assistance callbacks.
+ * @hide
+ */
+oneway interface IGnssAssistanceCallback {
+    void onError();
+    void onResult(in GnssAssistance result);
+}
diff --git a/location/java/android/location/provider/IGnssAssistanceProvider.aidl b/location/java/android/location/provider/IGnssAssistanceProvider.aidl
new file mode 100644
index 0000000..1796e9e
--- /dev/null
+++ b/location/java/android/location/provider/IGnssAssistanceProvider.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.provider.IGnssAssistanceCallback;
+
+/**
+ * Binder interface for services that implement GNSS assistance providers. Do not implement this
+ * directly, extend {@link GnssAssistanceProviderBase} instead.
+ * @hide
+ */
+oneway interface IGnssAssistanceProvider {
+    void request(in IGnssAssistanceCallback callback);
+}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
index 03a2101..218983a 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
@@ -31,7 +31,6 @@
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settingslib.widget.preference.selector.R;
-import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
 
 /**
  * Selector preference (checkbox or radio button) with an optional additional widget.
@@ -180,10 +179,8 @@
                     : getContext().getString(R.string.settings_label));
         }
 
-        if (Flags.allowSetTitleMaxLines()) {
-            TextView title = (TextView) holder.findViewById(android.R.id.title);
-            title.setMaxLines(mTitleMaxLines);
-        }
+        TextView title = (TextView) holder.findViewById(android.R.id.title);
+        title.setMaxLines(mTitleMaxLines);
     }
 
     /**
@@ -244,16 +241,12 @@
         setLayoutResource(R.layout.preference_selector_with_widget);
         setIconSpaceReserved(false);
 
-        if (Flags.allowSetTitleMaxLines()) {
-            final TypedArray a =
-                    context.obtainStyledAttributes(
-                            attrs, R.styleable.SelectorWithWidgetPreference, defStyleAttr,
-                            defStyleRes);
-            mTitleMaxLines =
-                    a.getInt(R.styleable.SelectorWithWidgetPreference_titleMaxLines,
-                            DEFAULT_MAX_LINES);
-            a.recycle();
-        }
+        final TypedArray a =
+                context.obtainStyledAttributes(
+                        attrs, R.styleable.SelectorWithWidgetPreference, defStyleAttr, defStyleRes);
+        mTitleMaxLines =
+                a.getInt(R.styleable.SelectorWithWidgetPreference_titleMaxLines, DEFAULT_MAX_LINES);
+        a.recycle();
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
index 2b8b3b7..c939c77 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
@@ -21,9 +21,6 @@
 import static org.junit.Assert.assertEquals;
 
 import android.app.Application;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -33,10 +30,8 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settingslib.widget.preference.selector.R;
-import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
@@ -45,7 +40,6 @@
 @RunWith(RobolectricTestRunner.class)
 public class SelectorWithWidgetPreferenceTest {
 
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private Application mContext;
     private SelectorWithWidgetPreference mPreference;
 
@@ -128,26 +122,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
-    public void onBindViewHolder_titleMaxLinesSet_flagOff_titleMaxLinesMatchesDefault() {
-        final int titleMaxLines = 5;
-        AttributeSet attributeSet = Robolectric.buildAttributeSet()
-                .addAttribute(R.attr.titleMaxLines, String.valueOf(titleMaxLines))
-                .build();
-        mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
-        View view = LayoutInflater.from(mContext)
-                .inflate(mPreference.getLayoutResource(), null /* root */);
-        PreferenceViewHolder preferenceViewHolder =
-                PreferenceViewHolder.createInstanceForTests(view);
-
-        mPreference.onBindViewHolder(preferenceViewHolder);
-
-        TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
-        assertThat(title.getMaxLines()).isEqualTo(SelectorWithWidgetPreference.DEFAULT_MAX_LINES);
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
     public void onBindViewHolder_noTitleMaxLinesSet_titleMaxLinesMatchesDefault() {
         AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
         mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
@@ -163,7 +137,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
     public void onBindViewHolder_titleMaxLinesSet_titleMaxLinesUpdated() {
         final int titleMaxLines = 5;
         AttributeSet attributeSet = Robolectric.buildAttributeSet()
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 91ac34a..de7c450 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -148,27 +148,7 @@
             // TODO(b/364399200): use filter to skip instead?
             return;
         }
-
-        ArrayList<String> missingFiles = new ArrayList<String>();
-        for (String fileName : sAconfigTextProtoFilesOnDevice) {
-            File aconfigFile = new File(fileName);
-            if (!aconfigFile.exists()) {
-                missingFiles.add(fileName);
-            }
-        }
-
-        if (missingFiles.isEmpty()) {
-            pw.println("\nAconfig flags:");
-            for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) {
-                pw.println(name);
-            }
-        } else {
-            pw.println("\nFailed to dump aconfig flags due to missing files:");
-            for (String fileName : missingFiles) {
-                pw.println(fileName);
-            }
-        }
-    }
+   }
 
     private static HashSet<String> getAconfigFlagNamesInDeviceConfig() {
         HashSet<String> nameSet = new HashSet<String>();
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c4d13ba..5ff2d1b 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -496,6 +496,13 @@
 }
 
 flag {
+    name: "status_bar_popup_chips"
+    namespace: "systemui"
+    description: "Show rich ongoing processes as chips in the status bar"
+    bug: "372964148"
+}
+
+flag {
     name: "promote_notifications_automatically"
     namespace: "systemui"
     description: "Flag to automatically turn certain notifications into promoted notifications so "
@@ -1237,6 +1244,13 @@
 }
 
 flag {
+    name: "glanceable_hub_back_action"
+    namespace: "systemui"
+    description: "Support back action from glanceable hub"
+    bug: "382771533"
+}
+
+flag {
     name: "dream_overlay_updated_font"
     namespace: "systemui"
     description: "Flag to enable updated font settings for dream overlay"
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index ca2b957..7d27a56 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -195,7 +195,10 @@
         // Create the origin leash and add to the transition root leash.
         mOriginLeash =
                 new SurfaceControl.Builder().setName("OriginTransition-origin-leash").build();
-        mStartTransaction
+
+        // Create temporary transaction to build
+        final SurfaceControl.Transaction tmpTransaction = new SurfaceControl.Transaction();
+        tmpTransaction
                 .reparent(mOriginLeash, rootLeash)
                 .show(mOriginLeash)
                 .setCornerRadius(mOriginLeash, windowRadius)
@@ -208,14 +211,14 @@
             int mode = change.getMode();
             SurfaceControl leash = change.getLeash();
             // Reparent leash to the transition root.
-            mStartTransaction.reparent(leash, rootLeash);
+            tmpTransaction.reparent(leash, rootLeash);
             if (TransitionUtil.isOpeningMode(mode)) {
                 openingSurfaces.add(change.getLeash());
                 // For opening surfaces, ending bounds are base bound. Apply corner radius if
                 // it's full screen.
                 Rect bounds = change.getEndAbsBounds();
                 if (displayBounds.equals(bounds)) {
-                    mStartTransaction
+                    tmpTransaction
                             .setCornerRadius(leash, windowRadius)
                             .setWindowCrop(leash, bounds.width(), bounds.height());
                 }
@@ -226,28 +229,53 @@
                 // it's full screen.
                 Rect bounds = change.getStartAbsBounds();
                 if (displayBounds.equals(bounds)) {
-                    mStartTransaction
+                    tmpTransaction
                             .setCornerRadius(leash, windowRadius)
                             .setWindowCrop(leash, bounds.width(), bounds.height());
                 }
             }
         }
 
+        if (openingSurfaces.isEmpty() && closingSurfaces.isEmpty()) {
+            logD("prepareUIs: no opening/closing surfaces available, nothing to prepare.");
+            return false;
+        }
+
         // Set relative order:
         // ----  App1  ----
         // ---- origin ----
         // ----  App2  ----
+
         if (mIsEntry) {
-            mStartTransaction
-                    .setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1)
-                    .setRelativeLayer(
-                            openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
+            if (!closingSurfaces.isEmpty()) {
+                tmpTransaction
+                        .setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1);
+            } else {
+                logW("Missing closing surface is entry transition");
+            }
+            if (!openingSurfaces.isEmpty()) {
+                tmpTransaction
+                        .setRelativeLayer(
+                                openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
+            } else {
+                logW("Missing opening surface is entry transition");
+            }
+
         } else {
-            mStartTransaction
-                    .setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1)
-                    .setRelativeLayer(
-                            closingSurfaces.get(closingSurfaces.size() - 1), mOriginLeash, 1);
+            if (!openingSurfaces.isEmpty()) {
+                tmpTransaction
+                        .setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1);
+            } else {
+                logW("Missing opening surface is exit transition");
+            }
+            if (!closingSurfaces.isEmpty()) {
+                tmpTransaction.setRelativeLayer(
+                        closingSurfaces.get(closingSurfaces.size() - 1), mOriginLeash, 1);
+            } else {
+                logW("Missing closing surface is exit transition");
+            }
         }
+        mStartTransaction.merge(tmpTransaction);
 
         // Attach origin UIComponent to origin leash.
         mOriginTransaction = mOrigin.newTransaction();
@@ -300,6 +328,7 @@
     }
 
     private void cancel() {
+        logD("cancel()");
         if (mAnimator != null) {
             mAnimator.cancel();
         }
@@ -311,6 +340,10 @@
         }
     }
 
+    private static void logW(String msg) {
+        Log.w(TAG, msg);
+    }
+
     private static void logE(String msg) {
         Log.e(TAG, msg);
     }
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 5dbedc7..bf3360f 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
@@ -931,7 +931,9 @@
                         Modifier.requiredSize(dpSize)
                             .thenIf(!isItemDragging) {
                                 Modifier.animateItem(
-                                    placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
+                                    placementSpec = spring(stiffness = Spring.StiffnessMediumLow),
+                                    // See b/376495198 - not supported with AndroidView
+                                    fadeOutSpec = null,
                                 )
                             }
                             .thenIf(isItemDragging) { Modifier.zIndex(1f) },
@@ -980,11 +982,14 @@
                     size = size,
                     selected = false,
                     modifier =
-                        Modifier.requiredSize(dpSize).animateItem().thenIf(
-                            communalResponsiveGrid()
-                        ) {
-                            Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
-                        },
+                        Modifier.requiredSize(dpSize)
+                            .animateItem(
+                                // See b/376495198 - not supported with AndroidView
+                                fadeOutSpec = null
+                            )
+                            .thenIf(communalResponsiveGrid()) {
+                                Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
+                            },
                     index = index,
                     contentListState = contentListState,
                     interactionHandler = interactionHandler,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 2af5ffa..5790c4a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.layoutId
 import com.android.compose.animation.scene.ContentScope
@@ -84,7 +85,11 @@
                 viewModel.notificationsPlaceholderViewModelFactory.create()
             }
 
-        OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
+        OverlayShade(
+            panelAlignment = Alignment.TopStart,
+            modifier = modifier,
+            onScrimClicked = viewModel::onScrimClicked,
+        ) {
             Column {
                 if (viewModel.showHeader) {
                     val burnIn = rememberBurnIn(clockInteractor)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index b1a1945..f6c5f58 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -99,7 +99,11 @@
         val viewModel =
             rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() }
 
-        OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
+        OverlayShade(
+            panelAlignment = Alignment.TopEnd,
+            modifier = modifier,
+            onScrimClicked = viewModel::onScrimClicked,
+        ) {
             Column {
                 ExpandedShadeHeader(
                     viewModelFactory = viewModel.shadeHeaderViewModelFactory,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index f821e42..cfbe667 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -55,16 +55,17 @@
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ContentScope
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexContentPicker
-import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.effect.rememberOffsetOverscrollEffect
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.res.R
 
 /** Renders a lightweight shade UI container, as an overlay. */
 @Composable
-fun SceneScope.OverlayShade(
+fun ContentScope.OverlayShade(
+    panelAlignment: Alignment,
     onScrimClicked: () -> Unit,
     modifier: Modifier = Modifier,
     content: @Composable () -> Unit,
@@ -87,7 +88,7 @@
     ) {
         Scrim(onClicked = onScrimClicked)
 
-        Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = Alignment.TopEnd) {
+        Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = panelAlignment) {
             Panel(
                 modifier =
                     Modifier.element(OverlayShade.Elements.Panel)
@@ -100,7 +101,7 @@
 }
 
 @Composable
-private fun SceneScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modifier) {
+private fun ContentScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modifier) {
     Spacer(
         modifier =
             modifier
@@ -112,7 +113,7 @@
 }
 
 @Composable
-private fun SceneScope.Panel(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
+private fun ContentScope.Panel(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
     Box(modifier = modifier.clip(OverlayShade.Shapes.RoundedCornerPanel)) {
         Spacer(
             modifier =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
index 85bdf92..cea1e96 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
@@ -163,6 +163,16 @@
     }
 
     @Test
+    fun testShow_rearDisplayOuterDefaultActive_occluded() {
+        displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+
+        whenever(deviceStateHelper.isRearDisplayOuterDefaultActive(secondaryDisplay))
+            .thenReturn(true)
+        whenever(keyguardStateController.isOccluded).thenReturn(true)
+        verify(presentationFactory, never()).create(eq(secondaryDisplay))
+    }
+
+    @Test
     fun testShow_presentationCreated() {
         displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 41cc6ee..271cd3a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.back.domain.interactor
 
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.annotations.RequiresFlagsDisabled
 import android.platform.test.annotations.RequiresFlagsEnabled
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
@@ -31,6 +32,7 @@
 import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.CommunalBackActionInteractor
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
@@ -93,6 +95,7 @@
     @Mock private lateinit var onBackInvokedDispatcher: WindowOnBackInvokedDispatcher
     @Mock private lateinit var iStatusBarService: IStatusBarService
     @Mock private lateinit var headsUpManager: HeadsUpManager
+    @Mock private lateinit var communalBackActionInteractor: CommunalBackActionInteractor
 
     private val keyguardRepository = FakeKeyguardRepository()
     private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
@@ -117,6 +120,7 @@
             windowRootViewVisibilityInteractor,
             shadeBackActionInteractor,
             qsController,
+            communalBackActionInteractor,
         )
     }
 
@@ -306,6 +310,19 @@
         verify(shadeBackActionInteractor).onBackProgressed(0.4f)
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_BACK_ACTION)
+    fun onBackAction_communalCanBeDismissed_communalBackActionInteractorCalled() {
+        backActionInteractor.start()
+        windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+        powerInteractor.setAwakeForTest()
+        val callback = getBackInvokedCallback()
+        whenever(communalBackActionInteractor.canBeDismissed()).thenReturn(true)
+        callback.onBackInvoked()
+
+        verify(communalBackActionInteractor).onBackPressed()
+    }
+
     private fun getBackInvokedCallback(): OnBackInvokedCallback {
         testScope.runCurrent()
         val captor = argumentCaptor<OnBackInvokedCallback>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt
new file mode 100644
index 0000000..c365f1c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class CommunalBackActionInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    private var Kosmos.underTest by Fixture { communalBackActionInteractor }
+
+    @Test
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    fun communalShowing_canBeDismissed() =
+        kosmos.runTest {
+            setCommunalAvailable(true)
+            assertThat(underTest.canBeDismissed()).isEqualTo(false)
+            communalInteractor.changeScene(CommunalScenes.Communal, "test")
+            runCurrent()
+            assertThat(underTest.canBeDismissed()).isEqualTo(true)
+        }
+
+    @Test
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    fun onBackPressed_invokesSceneChange() =
+        kosmos.runTest {
+            underTest.onBackPressed()
+            runCurrent()
+            assertThat(communalSceneRepository.currentScene.value).isEqualTo(CommunalScenes.Blank)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 0bfcd24..8a9c42d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -130,19 +130,6 @@
         }
 
     @Test
-    fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
-        testScope.runTest {
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
-            goToCommunal()
-
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
-        }
-
-    @Test
     fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
         testScope.runTest {
             val tutorialSettingState by
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index ee3e241..56e8185 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -81,7 +81,7 @@
         // Then
         verify(cameraGestureHelper)
             .launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
-        assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+        assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true), result)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 50ac2619..fde9b8c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -197,7 +197,7 @@
 
             val dndMode = currentModes!!.single()
             assertThat(dndMode.isActive).isFalse()
-            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
         }
 
     @Test
@@ -222,7 +222,7 @@
                 )
 
             // then
-            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
             assertEquals(ZEN_MODE_OFF, spyZenMode.value)
             assertNull(spyConditionId.value)
         }
@@ -244,7 +244,7 @@
             val dndMode = currentModes!!.single()
             assertThat(dndMode.isActive).isTrue()
             assertThat(zenModeRepository.getModeActiveDuration(dndMode.id)).isNull()
-            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
         }
 
     @Test
@@ -268,7 +268,7 @@
                 )
 
             // then
-            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
             assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
             assertNull(spyConditionId.value)
         }
@@ -285,7 +285,7 @@
             val result = underTest.onTriggered(null)
             runCurrent()
 
-            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
             val dndMode = currentModes!!.single()
             assertThat(dndMode.isActive).isTrue()
             assertThat(zenModeRepository.getModeActiveDuration(dndMode.id))
@@ -313,7 +313,7 @@
                 )
 
             // then
-            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
             assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
             assertEquals(conditionUri, spyConditionId.value)
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
new file mode 100644
index 0000000..18946f9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.domain.interactor.keyguardQuickAffordanceHapticViewModelFactory
+import com.android.systemui.keyguard.domain.interactor.keyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class KeyguardQuickAffordanceHapticViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+    private val configKey = "$slotId::home"
+    private val keyguardQuickAffordanceInteractor = kosmos.keyguardQuickAffordanceInteractor
+    private val viewModelFlow =
+        MutableStateFlow(KeyguardQuickAffordanceViewModel(configKey = configKey, slotId = slotId))
+
+    private val underTest =
+        kosmos.keyguardQuickAffordanceHapticViewModelFactory.create(viewModelFlow)
+
+    @Test
+    fun whenLaunchingFromTriggeredResult_hapticStateIsLaunch() =
+        testScope.runTest {
+            // GIVEN that the result from triggering the affordance launched an activity or dialog
+            val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+            keyguardQuickAffordanceInteractor.setLaunchingFromTriggeredResult(
+                KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(true, configKey)
+            )
+            runCurrent()
+
+            // THEN the haptic state indicates that a launch haptics must play
+            assertThat(hapticState)
+                .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.LAUNCH)
+        }
+
+    @Test
+    fun whenNotLaunchFromTriggeredResult_hapticStateDoesNotEmit() =
+        testScope.runTest {
+            // GIVEN that the result from triggering the affordance did not launch an activity or
+            // dialog
+            val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+            keyguardQuickAffordanceInteractor.setLaunchingFromTriggeredResult(
+                KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(false, configKey)
+            )
+            runCurrent()
+
+            // THEN there is no haptic state to play any feedback
+            assertThat(hapticState)
+                .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.NO_HAPTICS)
+        }
+
+    @Test
+    fun onQuickAffordanceTogglesToActivated_hapticStateIsToggleOn() =
+        testScope.runTest {
+            // GIVEN that an affordance toggles from deactivated to activated
+            val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+            toggleQuickAffordance(on = true)
+
+            // THEN the haptic state reflects that a toggle on haptics should play
+            assertThat(hapticState)
+                .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.TOGGLE_ON)
+        }
+
+    @Test
+    fun onQuickAffordanceTogglesToDeactivated_hapticStateIsToggleOff() =
+        testScope.runTest {
+            // GIVEN that an affordance toggles from activated to deactivated
+            val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+            toggleQuickAffordance(on = false)
+
+            // THEN the haptic state reflects that a toggle off haptics should play
+            assertThat(hapticState)
+                .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.TOGGLE_OFF)
+        }
+
+    private fun TestScope.toggleQuickAffordance(on: Boolean) {
+        underTest.updateActivatedHistory(!on)
+        runCurrent()
+        underTest.updateActivatedHistory(on)
+        runCurrent()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
index b15352b..173b4e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
@@ -49,14 +49,10 @@
 class MuteQuickAffordanceConfigTest : SysuiTestCase() {
 
     private lateinit var underTest: MuteQuickAffordanceConfig
-    @Mock
-    private lateinit var ringerModeTracker: RingerModeTracker
-    @Mock
-    private lateinit var audioManager: AudioManager
-    @Mock
-    private lateinit var userTracker: UserTracker
-    @Mock
-    private lateinit var userFileManager: UserFileManager
+    @Mock private lateinit var ringerModeTracker: RingerModeTracker
+    @Mock private lateinit var audioManager: AudioManager
+    @Mock private lateinit var userTracker: UserTracker
+    @Mock private lateinit var userFileManager: UserFileManager
 
     private lateinit var testDispatcher: TestDispatcher
     private lateinit var testScope: TestScope
@@ -70,9 +66,12 @@
 
         whenever(userTracker.userContext).thenReturn(context)
         whenever(userFileManager.getSharedPreferences(any(), any(), any()))
-                .thenReturn(context.getSharedPreferences("mutequickaffordancetest", Context.MODE_PRIVATE))
+            .thenReturn(
+                context.getSharedPreferences("mutequickaffordancetest", Context.MODE_PRIVATE)
+            )
 
-        underTest = MuteQuickAffordanceConfig(
+        underTest =
+            MuteQuickAffordanceConfig(
                 context,
                 userTracker,
                 userFileManager,
@@ -81,64 +80,71 @@
                 testScope.backgroundScope,
                 testDispatcher,
                 testDispatcher,
-        )
+            )
     }
 
     @Test
-    fun pickerState_volumeFixed_notAvailable() = testScope.runTest {
-        //given
-        whenever(audioManager.isVolumeFixed).thenReturn(true)
+    fun pickerState_volumeFixed_notAvailable() =
+        testScope.runTest {
+            // given
+            whenever(audioManager.isVolumeFixed).thenReturn(true)
 
-        //when
-        val result = underTest.getPickerScreenState()
+            // when
+            val result = underTest.getPickerScreenState()
 
-        //then
-        assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice, result)
-    }
+            // then
+            assertEquals(
+                KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice,
+                result,
+            )
+        }
 
     @Test
-    fun pickerState_volumeNotFixed_available() = testScope.runTest {
-        //given
-        whenever(audioManager.isVolumeFixed).thenReturn(false)
+    fun pickerState_volumeNotFixed_available() =
+        testScope.runTest {
+            // given
+            whenever(audioManager.isVolumeFixed).thenReturn(false)
 
-        //when
-        val result = underTest.getPickerScreenState()
+            // when
+            val result = underTest.getPickerScreenState()
 
-        //then
-        assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default(), result)
-    }
+            // then
+            assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default(), result)
+        }
 
     @Test
-    fun triggered_stateWasPreviouslyNORMAL_currentlySILENT_moveToPreviousState() = testScope.runTest {
-        //given
-        val ringerModeCapture = argumentCaptor<Int>()
-        whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
-        underTest.onTriggered(null)
-        whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
+    fun triggered_stateWasPreviouslyNORMAL_currentlySILENT_moveToPreviousState() =
+        testScope.runTest {
+            // given
+            val ringerModeCapture = argumentCaptor<Int>()
+            whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+            underTest.onTriggered(null)
+            whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
 
-        //when
-        val result = underTest.onTriggered(null)
-        runCurrent()
-        verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
+            // when
+            val result = underTest.onTriggered(null)
+            runCurrent()
+            verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
 
-        //then
-        assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
-        assertEquals(AudioManager.RINGER_MODE_NORMAL, ringerModeCapture.value)
-    }
+            // then
+            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
+            assertEquals(AudioManager.RINGER_MODE_NORMAL, ringerModeCapture.value)
+        }
 
     @Test
-    fun triggered_stateIsNotSILENT_moveToSILENTringer() = testScope.runTest {
-        //given
-        val ringerModeCapture = argumentCaptor<Int>()
-        whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+    fun triggered_stateIsNotSILENT_moveToSILENTringer() =
+        testScope.runTest {
+            // given
+            val ringerModeCapture = argumentCaptor<Int>()
+            whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
 
-        //when
-        val result = underTest.onTriggered(null)
-        runCurrent()
-        verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
+            // when
+            val result = underTest.onTriggered(null)
+            runCurrent()
+            verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
 
-        //then
-        assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
-        assertEquals(AudioManager.RINGER_MODE_SILENT, ringerModeCapture.value)
-    }
-}
\ No newline at end of file
+            // then
+            assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
+            assertEquals(AudioManager.RINGER_MODE_SILENT, ringerModeCapture.value)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index e9b36b8..9bdc363 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -88,9 +88,7 @@
                     Icon.Loaded(
                         drawable = ICON,
                         contentDescription =
-                            ContentDescription.Resource(
-                                res = R.string.accessibility_wallet_button,
-                            ),
+                            ContentDescription.Resource(res = R.string.accessibility_wallet_button),
                     )
                 )
         }
@@ -118,9 +116,7 @@
                     Icon.Loaded(
                         drawable = ICON,
                         contentDescription =
-                            ContentDescription.Resource(
-                                res = R.string.accessibility_wallet_button,
-                            ),
+                            ContentDescription.Resource(res = R.string.accessibility_wallet_button),
                     )
                 )
         }
@@ -163,13 +159,9 @@
         }
 
         assertThat(underTest.onTriggered(expandable))
-            .isEqualTo(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled)
+            .isEqualTo(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true))
         verify(walletController)
-            .startQuickAccessUiIntent(
-                activityStarter,
-                animationController,
-                /* hasCard= */ true,
-            )
+            .startQuickAccessUiIntent(activityStarter, animationController, /* hasCard= */ true)
     }
 
     @Test
@@ -184,9 +176,7 @@
     @Test
     fun getPickerScreenState_unavailable() =
         testScope.runTest {
-            setUpState(
-                isWalletServiceAvailable = false,
-            )
+            setUpState(isWalletServiceAvailable = false)
 
             assertThat(underTest.getPickerScreenState())
                 .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
@@ -195,9 +185,7 @@
     @Test
     fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() =
         testScope.runTest {
-            setUpState(
-                isWalletFeatureAvailable = false,
-            )
+            setUpState(isWalletFeatureAvailable = false)
 
             assertThat(underTest.getPickerScreenState())
                 .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
@@ -206,9 +194,7 @@
     @Test
     fun getPickerScreenState_disabledWhenThereIsNoCard() =
         testScope.runTest {
-            setUpState(
-                hasSelectedCard = false,
-            )
+            setUpState(hasSelectedCard = false)
 
             assertThat(underTest.getPickerScreenState())
                 .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
@@ -219,7 +205,7 @@
         isWalletServiceAvailable: Boolean = true,
         isWalletQuerySuccessful: Boolean = true,
         hasSelectedCard: Boolean = true,
-        cardType: Int = WalletCard.CARD_TYPE_UNKNOWN
+        cardType: Int = WalletCard.CARD_TYPE_UNKNOWN,
     ) {
         val walletClient: QuickAccessWalletClient = mock()
         whenever(walletClient.tileIcon).thenReturn(ICON)
@@ -242,11 +228,11 @@
                                             /*cardType= */ cardType,
                                             /*cardImage= */ mock(),
                                             /*contentDescription=  */ CARD_DESCRIPTION,
-                                            /*pendingIntent= */ mock()
+                                            /*pendingIntent= */ mock(),
                                         )
                                         .build()
                                 ),
-                                0
+                                0,
                             )
                         } else {
                             GetWalletCardsResponse(emptyList(), 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 46d1ebe..9de0215 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -764,6 +764,28 @@
             assertThat(launchingAffordance).isFalse()
         }
 
+    @Test
+    fun onQuickAffordanceTriggered_updatesLaunchingFromTriggeredResult() =
+        testScope.runTest {
+            // WHEN selecting and triggering a quick affordance at a slot
+            val key = homeControls.key
+            val slot = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+            val encodedKey = "$slot::$key"
+            val actionLaunched = true
+            homeControls.onTriggeredResult =
+                KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(actionLaunched)
+            underTest.select(slot, key)
+            runCurrent()
+            underTest.onQuickAffordanceTriggered(encodedKey, expandable = null, slot)
+
+            // THEN the latest triggered result shows that an action launched for the same key and
+            // slot
+            val launchingFromTriggeredResult by
+                collectLastValue(underTest.launchingFromTriggeredResult)
+            assertThat(launchingFromTriggeredResult?.launched).isEqualTo(actionLaunched)
+            assertThat(launchingFromTriggeredResult?.configKey).isEqualTo(encodedKey)
+        }
+
     companion object {
         private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
         private val ICON: Icon =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 0e3b03f..be504cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -18,11 +18,8 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.DisableSceneContainer
@@ -75,7 +72,6 @@
     private val burnInFlow = MutableStateFlow(BurnInModel())
 
     @Before
-    @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     @DisableSceneContainer
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -219,50 +215,6 @@
         }
 
     @Test
-    @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-    fun translationAndScale_whenFullyDozing_MigrationFlagOff_staysOutOfTopInset() =
-        testScope.runTest {
-            underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
-            val movement by collectLastValue(underTest.movement)
-            assertThat(movement?.translationX).isEqualTo(0)
-
-            // Set to dozing (on AOD)
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED,
-                ),
-                validateStep = false,
-            )
-
-            // Trigger a change to the burn-in model
-            burnInFlow.value = BurnInModel(translationX = 20, translationY = -30, scale = 0.5f)
-            assertThat(movement?.translationX).isEqualTo(20)
-            // -20 instead of -30, due to inset of 80
-            assertThat(movement?.translationY).isEqualTo(-20)
-            assertThat(movement?.scale).isEqualTo(0.5f)
-            assertThat(movement?.scaleClockOnly).isEqualTo(true)
-
-            // Set to the beginning of GONE->AOD transition
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 0f,
-                    transitionState = TransitionState.STARTED,
-                ),
-                validateStep = false,
-            )
-            assertThat(movement?.translationX).isEqualTo(0)
-            assertThat(movement?.translationY).isEqualTo(0)
-            assertThat(movement?.scale).isEqualTo(1f)
-            assertThat(movement?.scaleClockOnly).isEqualTo(true)
-        }
-
-    @Test
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun translationAndScale_whenFullyDozing_MigrationFlagOn_staysOutOfTopInset() =
         testScope.runTest {
             underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
@@ -334,7 +286,6 @@
 
     @Test
     @DisableSceneContainer
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun translationAndScale_sceneContainerOff_weatherLargeClock() =
         testBurnInViewModelForClocks(
             isSmallClock = false,
@@ -344,7 +295,6 @@
 
     @Test
     @DisableSceneContainer
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun translationAndScale_sceneContainerOff_weatherSmallClock() =
         testBurnInViewModelForClocks(
             isSmallClock = true,
@@ -354,7 +304,6 @@
 
     @Test
     @DisableSceneContainer
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun translationAndScale_sceneContainerOff_nonWeatherLargeClock() =
         testBurnInViewModelForClocks(
             isSmallClock = false,
@@ -364,7 +313,6 @@
 
     @Test
     @DisableSceneContainer
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun translationAndScale_sceneContainerOff_nonWeatherSmallClock() =
         testBurnInViewModelForClocks(
             isSmallClock = true,
@@ -373,7 +321,6 @@
         )
 
     @Test
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     @EnableSceneContainer
     fun translationAndScale_sceneContainerOn_weatherLargeClock() =
         testBurnInViewModelForClocks(
@@ -383,7 +330,6 @@
         )
 
     @Test
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     @EnableSceneContainer
     fun translationAndScale_sceneContainerOn_weatherSmallClock() =
         testBurnInViewModelForClocks(
@@ -393,7 +339,6 @@
         )
 
     @Test
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     @EnableSceneContainer
     fun translationAndScale_sceneContainerOn_nonWeatherLargeClock() =
         testBurnInViewModelForClocks(
@@ -403,7 +348,6 @@
         )
 
     @Test
-    @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     @EnableSceneContainer
     @Ignore("b/367659687")
     fun translationAndScale_sceneContainerOn_nonWeatherSmallClock() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 95ffc96..789477e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -19,12 +19,10 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.communalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -73,7 +71,6 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
 class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index de3dc57..d16342b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -28,6 +28,7 @@
 import com.android.settingslib.notification.modes.TestModeBuilder
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.SysuiTestableContext
+import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.asIcon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
@@ -63,7 +64,7 @@
     fun setUp() {
         context.orCreateTestableResources.apply {
             addOverride(MODES_DRAWABLE_ID, MODES_DRAWABLE)
-            addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE)
+            addOverride(BEDTIME_DRAWABLE_ID, BEDTIME_DRAWABLE)
         }
 
         val customPackageContext = SysuiTestableContext(context)
@@ -158,7 +159,7 @@
             zenModeRepository.addMode(
                 id = "Bedtime with default icon",
                 type = AutomaticZenRule.TYPE_BEDTIME,
-                active = true
+                active = true,
             )
             runCurrent()
             assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
@@ -259,12 +260,14 @@
         val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
         const val CUSTOM_DRAWABLE_ID = 12345
 
+        val BEDTIME_DRAWABLE_ID = R.drawable.ic_zen_mode_type_bedtime
+
         val MODES_DRAWABLE = TestStubDrawable("modes_icon")
         val BEDTIME_DRAWABLE = TestStubDrawable("bedtime")
         val CUSTOM_DRAWABLE = TestStubDrawable("custom")
 
-        val MODES_ICON = MODES_DRAWABLE.asIcon()
-        val BEDTIME_ICON = BEDTIME_DRAWABLE.asIcon()
+        val MODES_ICON = Icon.Loaded(MODES_DRAWABLE, null, MODES_DRAWABLE_ID)
+        val BEDTIME_ICON = Icon.Loaded(BEDTIME_DRAWABLE, null, BEDTIME_DRAWABLE_ID)
         val CUSTOM_ICON = CUSTOM_DRAWABLE.asIcon()
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 165e943..40f13bb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -16,10 +16,12 @@
 
 package com.android.systemui.statusbar.chips.notification.ui.viewmodel
 
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -71,6 +73,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_noNotifs_empty() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -81,6 +84,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_notifMissingStatusBarChipIconView_empty() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -99,6 +103,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_onePromotedNotif_statusBarIconViewMatches() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -122,6 +127,7 @@
 
     @Test
     @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_onePromotedNotif_connectedDisplaysFlagEnabled_statusBarIconMatches() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -145,6 +151,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_onePromotedNotif_colorMatches() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -175,6 +182,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_onlyForPromotedNotifs() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -208,6 +216,7 @@
 
     @Test
     @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_connectedDisplaysFlagEnabled_onlyForPromotedNotifs() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -242,6 +251,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_hasShortCriticalText_usesTextInsteadOfTime() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -272,6 +282,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_noTime_isIconOnly() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -294,6 +305,36 @@
         }
 
     @Test
+    @EnableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+    fun chips_basicTime_hiddenIfAutomaticallyPromoted() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply {
+                    this.time =
+                        PromotedNotificationContentModel.When(
+                            time = 6543L,
+                            mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+                        )
+                }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        promotedContent = promotedContentBuilder.build(),
+                    )
+                )
+            )
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0])
+                .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+        }
+
+    @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_basicTime_isShortTimeDelta() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -322,6 +363,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_countUpTime_isTimer() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -349,6 +391,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_countDownTime_isTimer() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -376,6 +419,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_noHeadsUp_showsTime() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -407,6 +451,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_hasHeadsUpByUser_onlyShowsIcon() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
@@ -442,6 +487,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_clickingChipNotifiesInteractor() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
new file mode 100644
index 0000000..14787e1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarPopupChips
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@EnableFlags(StatusBarPopupChips.FLAG_NAME)
+@RunWith(AndroidJUnit4::class)
+class StatusBarPopupChipsViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val underTest = kosmos.statusBarPopupChipsViewModel
+
+    @Test
+    fun popupChips_allHidden_empty() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.popupChips)
+            assertThat(latest).isEmpty()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index f48fd3c..6bdd86e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -241,7 +241,7 @@
             shadeTestUtil.setSplitShade(true)
 
             val horizontalPosition = checkNotNull(dimens).horizontalPosition
-            assertIs<HorizontalPosition.FloatAtEnd>(horizontalPosition)
+            assertIs<HorizontalPosition.FloatAtStart>(horizontalPosition)
             assertThat(horizontalPosition.width).isEqualTo(200)
         }
 
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index ab0f788..ec24c3d 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -23,6 +23,9 @@
     <!-- The maximum number of rows in the QuickSettings -->
     <integer name="quick_settings_max_rows">4</integer>
 
+    <!-- The number of columns in the Split Shade QuickSettings -->
+    <integer name="quick_settings_split_shade_num_columns">6</integer>
+
     <!-- Use collapsed layout for media player in landscape QQS -->
     <bool name="config_quickSettingsMediaLandscapeCollapsed">false</bool>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index fc536bd..6f13d63 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
+import static com.android.systemui.Flags.glanceableHubBackAction;
 import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
 
 import android.annotation.LongDef;
@@ -352,6 +353,10 @@
         }
         // Disable back gesture on the hub, but not when the shade is showing.
         if ((sysuiStateFlags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
+            // Allow back gesture on Glanceable Hub with back action support.
+            if (glanceableHubBackAction()) {
+                return false;
+            }
             // Use QS expanded signal as the notification panel is always considered visible
             // expanded when on the lock screen and when opening hub over lock screen. This does
             // mean that back gesture is disabled when opening shade over hub while in portrait
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 1083136..acfa086 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -26,11 +26,9 @@
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteInfo;
 import android.os.Trace;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
-import android.view.DisplayAddress;
 import android.view.DisplayInfo;
 import android.view.View;
 import android.view.WindowManager;
@@ -58,6 +56,9 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
+/**
+ * Manages Keyguard Presentations for non-primary display(s).
+ */
 @SysUISingleton
 public class KeyguardDisplayManager {
     protected static final String TAG = "KeyguardDisplayManager";
@@ -170,14 +171,17 @@
             }
             return false;
         }
-        if (mKeyguardStateController.isOccluded()
-                && mDeviceStateHelper.isConcurrentDisplayActive(display)) {
+
+        final boolean deviceStateOccludesKeyguard =
+                mDeviceStateHelper.isConcurrentDisplayActive(display)
+                        || mDeviceStateHelper.isRearDisplayOuterDefaultActive(display);
+        if (mKeyguardStateController.isOccluded() && deviceStateOccludesKeyguard) {
             if (DEBUG) {
                 // When activities with FLAG_SHOW_WHEN_LOCKED are shown on top of Keyguard, the
                 // Keyguard state becomes "occluded". In this case, we should not show the
                 // KeyguardPresentation, since the activity is presenting content onto the
                 // non-default display.
-                Log.i(TAG, "Do not show KeyguardPresentation when occluded and concurrent"
+                Log.i(TAG, "Do not show KeyguardPresentation when occluded and concurrent or rear"
                         + " display is active");
             }
             return false;
@@ -326,44 +330,45 @@
     public static class DeviceStateHelper implements DeviceStateManager.DeviceStateCallback {
 
         @Nullable
-        private final DisplayAddress.Physical mRearDisplayPhysicalAddress;
-
-        // TODO(b/271317597): These device states should be defined in DeviceStateManager
-        private final int mConcurrentState;
-        private boolean mIsInConcurrentDisplayState;
+        private DeviceState mDeviceState;
 
         @Inject
         DeviceStateHelper(
-                @ShadeDisplayAware Context context,
                 DeviceStateManager deviceStateManager,
                 @Main Executor mainExecutor) {
-
-            final String rearDisplayPhysicalAddress = context.getResources().getString(
-                    com.android.internal.R.string.config_rearDisplayPhysicalAddress);
-            if (TextUtils.isEmpty(rearDisplayPhysicalAddress)) {
-                mRearDisplayPhysicalAddress = null;
-            } else {
-                mRearDisplayPhysicalAddress = DisplayAddress
-                        .fromPhysicalDisplayId(Long.parseLong(rearDisplayPhysicalAddress));
-            }
-
-            mConcurrentState = context.getResources().getInteger(
-                    com.android.internal.R.integer.config_deviceStateConcurrentRearDisplay);
             deviceStateManager.registerCallback(mainExecutor, this);
         }
 
         @Override
         public void onDeviceStateChanged(@NonNull DeviceState state) {
-            // When concurrent state ends, the display also turns off. This is enforced in various
-            // ExtensionRearDisplayPresentationTest CTS tests. So, we don't need to invoke
-            // hide() since that will happen through the onDisplayRemoved callback.
-            mIsInConcurrentDisplayState = state.getIdentifier() == mConcurrentState;
+            // When dual display or rear display mode ends, the display also turns off. This is
+            // enforced in various ExtensionRearDisplayPresentationTest CTS tests. So, we don't need
+            // to invoke hide() since that will happen through the onDisplayRemoved callback.
+            mDeviceState = state;
         }
 
-        boolean isConcurrentDisplayActive(Display display) {
-            return mIsInConcurrentDisplayState
-                    && mRearDisplayPhysicalAddress != null
-                    && mRearDisplayPhysicalAddress.equals(display.getAddress());
+        /**
+         * @return true if the device is in Dual Display mode, and the specified display is the
+         * rear facing (outer) display.
+         */
+        boolean isConcurrentDisplayActive(@NonNull Display display) {
+            return mDeviceState != null
+                    && mDeviceState.hasProperty(
+                            DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT)
+                    && (display.getFlags() & Display.FLAG_REAR) != 0;
+        }
+
+        /**
+         * @return true if the device is the updated Rear Display mode, and the specified display is
+         * the inner display. See {@link DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT}.
+         * Note that in this state, the outer display is the default display, while the inner
+         * display is the "rear" display.
+         */
+        boolean isRearDisplayOuterDefaultActive(@NonNull Display display) {
+            return mDeviceState != null
+                    && mDeviceState.hasProperty(
+                            DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)
+                    && (display.getFlags() & Display.FLAG_REAR) != 0;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 07bd813..40a86dc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -19,13 +19,12 @@
 import android.content.Context
 import android.view.View
 import com.android.systemui.customization.R as customR
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
-import com.android.systemui.shared.R as sharedR
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shared.R as sharedR
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START
@@ -55,16 +54,17 @@
     var statusViewCentered = false
 
     private val filterKeyguardAndSplitShadeOnly: () -> Boolean = {
-        statusBarStateController.getState() == KEYGUARD && !statusViewCentered }
+        statusBarStateController.getState() == KEYGUARD && !statusViewCentered
+    }
     private val filterKeyguard: () -> Boolean = { statusBarStateController.getState() == KEYGUARD }
 
     private val translateAnimator by lazy {
-        val smartSpaceViews = if (MigrateClocksToBlueprint.isEnabled) {
-            // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer]
-            val scrollXTranslation = { view: View, translation: Float ->
-                view.scrollX = -translation.toInt()
-            }
+        // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer]
+        val scrollXTranslation = { view: View, translation: Float ->
+            view.scrollX = -translation.toInt()
+        }
 
+        val smartSpaceViews =
             setOf(
                 ViewIdToTranslate(
                     viewId = sharedR.id.date_smartspace_view,
@@ -83,18 +83,8 @@
                     direction = START,
                     shouldBeAnimated = filterKeyguard,
                     translateFunc = scrollXTranslation,
-                )
+                ),
             )
-        } else {
-            setOf(ViewIdToTranslate(
-                viewId = R.id.keyguard_status_area,
-                direction = START,
-                shouldBeAnimated = filterKeyguard,
-                translateFunc = { view, value ->
-                    (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value
-                }
-            ))
-        }
 
         UnfoldConstantTranslateAnimator(
             viewsIdToTranslate =
@@ -102,39 +92,39 @@
                     ViewIdToTranslate(
                         viewId = customR.id.lockscreen_clock_view_large,
                         direction = START,
-                        shouldBeAnimated = filterKeyguardAndSplitShadeOnly
+                        shouldBeAnimated = filterKeyguardAndSplitShadeOnly,
                     ),
                     ViewIdToTranslate(
                         viewId = customR.id.lockscreen_clock_view,
                         direction = START,
-                        shouldBeAnimated = filterKeyguard
+                        shouldBeAnimated = filterKeyguard,
                     ),
                     ViewIdToTranslate(
                         viewId = R.id.notification_stack_scroller,
                         direction = END,
-                        shouldBeAnimated = filterKeyguardAndSplitShadeOnly
-                    )
+                        shouldBeAnimated = filterKeyguardAndSplitShadeOnly,
+                    ),
                 ) + smartSpaceViews,
-            progressProvider = unfoldProgressProvider
+            progressProvider = unfoldProgressProvider,
         )
     }
 
     private val shortcutButtonsAnimator by lazy {
         UnfoldConstantTranslateAnimator(
             viewsIdToTranslate =
-            setOf(
-                ViewIdToTranslate(
-                    viewId = R.id.start_button,
-                    direction = START,
-                    shouldBeAnimated = filterKeyguard
+                setOf(
+                    ViewIdToTranslate(
+                        viewId = R.id.start_button,
+                        direction = START,
+                        shouldBeAnimated = filterKeyguard,
+                    ),
+                    ViewIdToTranslate(
+                        viewId = R.id.end_button,
+                        direction = END,
+                        shouldBeAnimated = filterKeyguard,
+                    ),
                 ),
-                ViewIdToTranslate(
-                    viewId = R.id.end_button,
-                    direction = END,
-                    shouldBeAnimated = filterKeyguard
-                )
-            ),
-            progressProvider = unfoldProgressProvider
+            progressProvider = unfoldProgressProvider,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 232b629..47910f3 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -21,8 +21,11 @@
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import android.window.WindowOnBackInvokedDispatcher
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.glanceableHubBackAction
 import com.android.systemui.Flags.predictiveBackAnimateShade
+import com.android.systemui.communal.domain.interactor.CommunalBackActionInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -35,7 +38,6 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Handles requests to go back either from a button or gesture. */
 @SysUISingleton
@@ -50,6 +52,7 @@
     private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
     private val shadeBackActionInteractor: ShadeBackActionInteractor,
     private val qsController: QuickSettingsController,
+    private val communalBackActionInteractor: CommunalBackActionInteractor,
 ) : CoreStartable {
 
     private var isCallbackRegistered = false
@@ -114,6 +117,12 @@
         if (shadeBackActionInteractor.closeUserSwitcherIfOpen()) {
             return true
         }
+        if (glanceableHubBackAction()) {
+            if (communalBackActionInteractor.canBeDismissed()) {
+                communalBackActionInteractor.onBackPressed()
+                return true
+            }
+        }
         if (shouldBackBeHandled()) {
             if (shadeBackActionInteractor.canBeCollapsed()) {
                 // this is the Shade dismiss animation, so make sure QQS closes when it ends.
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index aef5f1f..e6f0245 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -21,14 +21,17 @@
 
 /**
  * Models an icon, that can either be already [loaded][Icon.Loaded] or be a [reference]
- * [Icon.Resource] to a resource.
+ * [Icon.Resource] to a resource. In case of [Loaded], the resource ID [res] is optional.
  */
 sealed class Icon {
     abstract val contentDescription: ContentDescription?
 
-    data class Loaded(
+    data class Loaded
+    @JvmOverloads
+    constructor(
         val drawable: Drawable,
         override val contentDescription: ContentDescription?,
+        @DrawableRes val res: Int? = null,
     ) : Icon()
 
     data class Resource(
@@ -37,6 +40,11 @@
     ) : Icon()
 }
 
-/** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */
-fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon.Loaded =
-    Icon.Loaded(this, contentDescription)
+/**
+ * Creates [Icon.Loaded] for a given drawable with an optional [contentDescription] and an optional
+ * [res].
+ */
+fun Drawable.asIcon(
+    contentDescription: ContentDescription? = null,
+    @DrawableRes res: Int? = null,
+): Icon.Loaded = Icon.Loaded(this, contentDescription, res)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt
new file mode 100644
index 0000000..2ccf96a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+
+/**
+ * {@link CommunalBackActionInteractor} is responsible for handling back gestures on the glanceable
+ * hub. When invoked SystemUI should navigate back to the lockscreen.
+ */
+@SysUISingleton
+class CommunalBackActionInteractor
+@Inject
+constructor(
+    private val communalInteractor: CommunalInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
+    private val sceneInteractor: SceneInteractor,
+) {
+    fun canBeDismissed(): Boolean {
+        return communalInteractor.isCommunalShowing.value
+    }
+
+    fun onBackPressed() {
+        if (SceneContainerFlag.isEnabled) {
+            // TODO(b/384610333): Properly determine whether to go to dream or lockscreen on back.
+            sceneInteractor.changeScene(
+                toScene = Scenes.Lockscreen,
+                loggingReason = "CommunalBackActionInteractor",
+            )
+        } else {
+            communalSceneInteractor.changeScene(
+                newScene = CommunalScenes.Blank,
+                loggingReason = "CommunalBackActionInteractor",
+            )
+        }
+    }
+}
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 ea42869..947113d 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
@@ -285,7 +285,7 @@
      * use [isIdleOnCommunal].
      */
     // TODO(b/323215860): rename to something more appropriate after cleaning up usages
-    val isCommunalShowing: Flow<Boolean> =
+    val isCommunalShowing: StateFlow<Boolean> =
         flow { emit(SceneContainerFlag.isEnabled) }
             .flatMapLatest { sceneContainerEnabled ->
                 if (sceneContainerEnabled) {
@@ -304,10 +304,10 @@
                 columnName = "isCommunalShowing",
                 initialValue = false,
             )
-            .shareIn(
+            .stateIn(
                 scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                replay = 1,
+                started = SharingStarted.Eagerly,
+                initialValue = false,
             )
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 9ae106c..014c0db 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -267,6 +267,7 @@
     }
 
     @Provides
+    @Nullable
     @Singleton
     static VirtualDeviceManager provideVirtualDeviceManager(Context context) {
         return context.getSystemService(VirtualDeviceManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 571b37f..b272d65 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -54,6 +54,7 @@
 import com.android.internal.policy.PhoneWindow;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Flags;
 import com.android.systemui.ambient.touch.TouchHandler;
 import com.android.systemui.ambient.touch.TouchMonitor;
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
@@ -210,6 +211,7 @@
                 mCommunalVisible = communalVisible;
 
                 updateLifecycleStateLocked();
+                updateGestureBlockingLocked();
             });
         }
     };
@@ -585,7 +587,8 @@
 
     private void updateGestureBlockingLocked() {
         final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing
-                && !isDreamInPreviewMode();
+                && !isDreamInPreviewMode()
+                && !(Flags.glanceableHubBackAction() && mCommunalVisible);
 
         if (shouldBlock) {
             mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index f549e64..d0065c8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -39,7 +39,6 @@
 import androidx.core.math.MathUtils
 import com.android.app.animation.Interpolators
 import com.android.internal.R
-import com.android.keyguard.KeyguardClockSwitchController
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.Flags.fasterUnlockTransition
 import com.android.systemui.dagger.SysUISingleton
@@ -206,7 +205,7 @@
         fun onUnlockAnimationFinished() {}
     }
 
-    /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */
+    /** The SmartSpace view on the lockscreen. */
     var lockscreenSmartspace: View? = null
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index 74ee052..57f06fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -21,13 +21,13 @@
 import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.pm.PackageManager
-import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.camera.CameraGestureHelper
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shade.ShadeDisplayAware
 import dagger.Lazy
@@ -65,7 +65,7 @@
                         icon =
                             Icon.Resource(
                                 R.drawable.ic_camera,
-                                ContentDescription.Resource(R.string.accessibility_camera_button)
+                                ContentDescription.Resource(R.string.accessibility_camera_button),
                             )
                     )
                 } else {
@@ -88,7 +88,7 @@
         cameraGestureHelper
             .get()
             .launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
-        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
     }
 
     private suspend fun isLaunchable(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index e8d3bfa..1b8baf6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -210,16 +210,16 @@
     ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
         return if (ModesUi.isEnabled) {
             if (!isAvailable.value) {
-                KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
             } else {
                 val dnd = interactor.dndMode.value
                 if (dnd == null) {
                     Log.wtf(TAG, "Triggered DND but it's null!?")
-                    return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                    return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                 }
                 if (dnd.isActive) {
                     interactor.deactivateMode(dnd)
-                    return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                    return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                 } else {
                     if (interactor.shouldAskForZenDuration(dnd)) {
                         // NOTE: The dialog handles turning on the mode itself.
@@ -229,16 +229,16 @@
                         )
                     } else {
                         interactor.activateMode(dnd)
-                        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                     }
                 }
             }
         } else {
             when {
-                !oldIsAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                !oldIsAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                 zenMode != ZEN_MODE_OFF -> {
                     controller.setZen(ZEN_MODE_OFF, null, TAG)
-                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                 }
 
                 settingsValue == ZEN_DURATION_PROMPT ->
@@ -249,12 +249,12 @@
 
                 settingsValue == ZEN_DURATION_FOREVER -> {
                     controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG)
-                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                 }
 
                 else -> {
                     controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, conditionUri, TAG)
-                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index 480ef5e..e2642a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -18,15 +18,14 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.content.Context
-import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.policy.FlashlightController
 import javax.inject.Inject
@@ -50,9 +49,9 @@
                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                     Icon.Resource(
                         R.drawable.qs_flashlight_icon_on,
-                        ContentDescription.Resource(R.string.quick_settings_flashlight_label)
+                        ContentDescription.Resource(R.string.quick_settings_flashlight_label),
                     ),
-                    ActivationState.Active
+                    ActivationState.Active,
                 )
         }
 
@@ -61,9 +60,9 @@
                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                     Icon.Resource(
                         R.drawable.qs_flashlight_icon_off,
-                        ContentDescription.Resource(R.string.quick_settings_flashlight_label)
+                        ContentDescription.Resource(R.string.quick_settings_flashlight_label),
                     ),
-                    ActivationState.Inactive
+                    ActivationState.Inactive,
                 )
         }
 
@@ -92,14 +91,14 @@
                             } else {
                                 FlashlightState.OffAvailable.toLockScreenState()
                             },
-                            TAG
+                            TAG,
                         )
                     }
 
                     override fun onFlashlightError() {
                         trySendWithFailureLogging(
                             FlashlightState.OffAvailable.toLockScreenState(),
-                            TAG
+                            TAG,
                         )
                     }
 
@@ -114,7 +113,7 @@
                                     FlashlightState.OffAvailable.toLockScreenState()
                                 }
                             },
-                            TAG
+                            TAG,
                         )
                     }
                 }
@@ -130,7 +129,7 @@
         flashlightController.setFlashlight(
             flashlightController.isAvailable && !flashlightController.isEnabled
         )
-        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
     }
 
     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
index d335a18..06da281 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -111,7 +111,7 @@
                 transitionKey = CommunalTransitionKeys.SimpleFade,
             )
         }
-        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 1cf6183..ade65c3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -21,10 +21,10 @@
 import android.content.Context
 import android.content.Intent
 import android.net.Uri
-import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
 import kotlinx.coroutines.flow.Flow
 
 /** Defines interface that can act as data source for a single quick affordance model. */
@@ -71,7 +71,7 @@
         /** The picker shows the item for selecting this affordance as it normally would. */
         data class Default(
             /** Optional [Intent] to use to start an activity to configure this affordance. */
-            val configureIntent: Intent? = null,
+            val configureIntent: Intent? = null
         ) : PickerScreenState()
 
         /**
@@ -134,34 +134,39 @@
         ) : LockScreenState()
     }
 
-    sealed class OnTriggeredResult {
+    sealed class OnTriggeredResult() {
         /**
          * Returning this as a result from the [onTriggered] method means that the implementation
          * has taken care of the action, the system will do nothing.
+         *
+         * @param[actionLaunched] Whether the implementation handled the action by launching a
+         *   dialog or an activity.
          */
-        object Handled : OnTriggeredResult()
+        data class Handled(val actionLaunched: Boolean) : OnTriggeredResult()
 
         /**
          * Returning this as a result from the [onTriggered] method means that the implementation
          * has _not_ taken care of the action and the system should start an activity using the
          * given [Intent].
          */
-        data class StartActivity(
-            val intent: Intent,
-            val canShowWhileLocked: Boolean,
-        ) : OnTriggeredResult()
+        data class StartActivity(val intent: Intent, val canShowWhileLocked: Boolean) :
+            OnTriggeredResult()
 
         /**
          * Returning this as a result from the [onTriggered] method means that the implementation
          * has _not_ taken care of the action and the system should show a Dialog using the given
          * [AlertDialog] and [Expandable].
          */
-        data class ShowDialog(
-            val dialog: AlertDialog,
-            val expandable: Expandable?,
-        ) : OnTriggeredResult()
+        data class ShowDialog(val dialog: AlertDialog, val expandable: Expandable?) :
+            OnTriggeredResult()
     }
 
+    /**
+     * Models an [OnTriggeredResult] that did or did not launch a dialog or activity for a given
+     * config key.
+     */
+    data class LaunchingFromTriggeredResult(val launched: Boolean, val configKey: String)
+
     companion object {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
index 1358634..1c9bc9f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -21,6 +21,7 @@
 import android.media.AudioManager
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.Observer
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
@@ -45,7 +46,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
 import kotlinx.coroutines.withContext
 
 @SysUISingleton
@@ -118,7 +118,7 @@
                 audioManager.ringerModeInternal = newRingerMode
             }
         }
-        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
     }
 
     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
@@ -140,11 +140,11 @@
                 .getSharedPreferences(
                     MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
                     Context.MODE_PRIVATE,
-                    userTracker.userId
+                    userTracker.userId,
                 )
                 .getInt(
                     LAST_NON_SILENT_RINGER_MODE_KEY,
-                    ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
+                    ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE,
                 )
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index eafa1ce..cb7702e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
@@ -72,21 +71,15 @@
                         override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
                             val hasCards =
                                 getPaymentCards(response.walletCards)?.isNotEmpty() == true
-                            trySendWithFailureLogging(
-                                hasCards,
-                                TAG,
-                            )
+                            trySendWithFailureLogging(hasCards, TAG)
                         }
 
                         override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
                             Log.e(
                                 TAG,
-                                "Wallet card retrieval error, message: \"${error?.message}\""
+                                "Wallet card retrieval error, message: \"${error?.message}\"",
                             )
-                            trySendWithFailureLogging(
-                                null,
-                                TAG,
-                            )
+                            trySendWithFailureLogging(null, TAG)
                         }
                     }
 
@@ -94,7 +87,7 @@
                     callback,
                     QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
                     QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE,
-                    QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE
+                    QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE,
                 )
 
                 withContext(backgroundDispatcher) {
@@ -107,7 +100,7 @@
                     walletController.unregisterWalletChangeObservers(
                         QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
                         QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE,
-                        QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE
+                        QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE,
                     )
                 }
             }
@@ -117,11 +110,7 @@
                     if (hasCards == null) {
                         KeyguardQuickAffordanceConfig.LockScreenState.Hidden
                     } else {
-                        state(
-                            isWalletAvailable(),
-                            hasCards,
-                            walletController.walletClient.tileIcon,
-                        )
+                        state(isWalletAvailable(), hasCards, walletController.walletClient.tileIcon)
                     }
                 flowOf(state)
             }
@@ -135,28 +124,28 @@
                     explanation =
                         context.getString(
                             R.string.wallet_quick_affordance_unavailable_install_the_app
-                        ),
+                        )
                 )
             queryCards().isEmpty() ->
                 KeyguardQuickAffordanceConfig.PickerScreenState.Disabled(
                     explanation =
                         context.getString(
                             R.string.wallet_quick_affordance_unavailable_configure_the_app
-                        ),
+                        )
                 )
             else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default()
         }
     }
 
     override fun onTriggered(
-        expandable: Expandable?,
+        expandable: Expandable?
     ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
         walletController.startQuickAccessUiIntent(
             activityStarter,
             expandable?.activityTransitionController(),
             /* hasCard= */ true,
         )
-        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
     }
 
     private suspend fun queryCards(): List<WalletCard> {
@@ -199,10 +188,8 @@
                     Icon.Loaded(
                         drawable = tileIcon,
                         contentDescription =
-                            ContentDescription.Resource(
-                                res = R.string.accessibility_wallet_button,
-                            ),
-                    ),
+                            ContentDescription.Resource(res = R.string.accessibility_wallet_button),
+                    )
             )
         } else {
             KeyguardQuickAffordanceConfig.LockScreenState.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index ae55825..9c2daf5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
 import com.android.systemui.dock.DockManager
@@ -62,6 +61,7 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
@@ -101,6 +101,14 @@
     val launchingAffordance: StateFlow<Boolean> = repository.get().launchingAffordance.asStateFlow()
 
     /**
+     * Whether a [KeyguardQuickAffordanceConfig.OnTriggeredResult] indicated that the system
+     * launched an activity or showed a dialog.
+     */
+    private val _launchingFromTriggeredResult =
+        MutableStateFlow<KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult?>(null)
+    val launchingFromTriggeredResult = _launchingFromTriggeredResult.asStateFlow()
+
+    /**
      * Whether the UI should use the long press gesture to activate quick affordances.
      *
      * If `false`, the UI goes back to using single taps.
@@ -187,18 +195,45 @@
         metricsLogger.logOnShortcutTriggered(slotId, configKey)
 
         when (val result = config.onTriggered(expandable)) {
-            is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
+            is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity -> {
+                setLaunchingFromTriggeredResult(
+                    KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+                        launched = true,
+                        configKey,
+                    )
+                )
                 launchQuickAffordance(
                     intent = result.intent,
                     canShowWhileLocked = result.canShowWhileLocked,
                     expandable = expandable,
                 )
-            is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> Unit
-            is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog ->
+            }
+            is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> {
+                setLaunchingFromTriggeredResult(
+                    KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+                        result.actionLaunched,
+                        configKey,
+                    )
+                )
+            }
+            is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog -> {
+                setLaunchingFromTriggeredResult(
+                    KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+                        launched = true,
+                        configKey,
+                    )
+                )
                 showDialog(result.dialog, result.expandable)
+            }
         }
     }
 
+    fun setLaunchingFromTriggeredResult(
+        launchingResult: KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult?
+    ) {
+        _launchingFromTriggeredResult.value = launchingResult
+    }
+
     /**
      * Selects an affordance with the given ID on the slot with the given ID.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
index e7803c5..a4a5ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
@@ -17,12 +17,23 @@
 package com.android.systemui.keyguard.ui.binder
 
 import android.os.VibrationEffect
+import com.android.systemui.Flags
 import kotlin.time.Duration.Companion.milliseconds
 
 object KeyguardBottomAreaVibrations {
 
-    val ShakeAnimationDuration = 300.milliseconds
-    const val ShakeAnimationCycles = 5f
+    val ShakeAnimationDuration =
+        if (Flags.msdlFeedback()) {
+            285.milliseconds
+        } else {
+            300.milliseconds
+        }
+    val ShakeAnimationCycles =
+        if (Flags.msdlFeedback()) {
+            3f
+        } else {
+            5f
+        }
 
     private const val SmallVibrationScale = 0.3f
     private const val BigVibrationScale = 0.6f
@@ -32,7 +43,7 @@
             .apply {
                 val vibrationDelayMs =
                     (ShakeAnimationDuration.inWholeMilliseconds / (ShakeAnimationCycles * 2))
-                    .toInt()
+                        .toInt()
 
                 val vibrationCount = ShakeAnimationCycles.toInt() * 2
                 repeat(vibrationCount) {
@@ -47,29 +58,13 @@
 
     val Activated =
         VibrationEffect.startComposition()
-            .addPrimitive(
-                VibrationEffect.Composition.PRIMITIVE_TICK,
-                BigVibrationScale,
-                0,
-            )
-            .addPrimitive(
-                VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
-                0.1f,
-                0,
-            )
+            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0)
+            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.1f, 0)
             .compose()
 
     val Deactivated =
         VibrationEffect.startComposition()
-            .addPrimitive(
-                VibrationEffect.Composition.PRIMITIVE_TICK,
-                BigVibrationScale,
-                0,
-            )
-            .addPrimitive(
-                VibrationEffect.Composition.PRIMITIVE_QUICK_FALL,
-                0.1f,
-                0,
-            )
+            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0)
+            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.1f, 0)
             .compose()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 8725cdd..8a2e3dd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -20,6 +20,7 @@
 import android.annotation.SuppressLint
 import android.content.res.ColorStateList
 import android.graphics.drawable.Animatable2
+import android.os.VibrationEffect
 import android.util.Size
 import android.view.View
 import android.view.ViewGroup
@@ -33,25 +34,27 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
+import com.android.systemui.Flags
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.util.doOnEnd
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.MSDLPlayer
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** This is only for a SINGLE Quick affordance */
 @SysUISingleton
@@ -60,8 +63,9 @@
 constructor(
     private val falsingManager: FalsingManager?,
     private val vibratorHelper: VibratorHelper?,
+    private val msdlPlayer: MSDLPlayer,
     private val logger: KeyguardQuickAffordancesLogger,
-    @Main private val mainImmediateDispatcher: CoroutineDispatcher,
+    private val hapticsViewModelFactory: KeyguardQuickAffordanceHapticViewModel.Factory,
 ) {
 
     private val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
@@ -88,6 +92,12 @@
     ): Binding {
         val button = view as ImageView
         val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
+        val hapticsViewModel =
+            if (Flags.msdlFeedback()) {
+                hapticsViewModelFactory.create(viewModel)
+            } else {
+                null
+            }
         val disposableHandle =
             view.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -98,15 +108,12 @@
                                 viewModel = buttonModel,
                                 messageDisplayer = messageDisplayer,
                             )
+                            hapticsViewModel?.updateActivatedHistory(buttonModel.isActivated)
                         }
                     }
 
                     launch {
-                        updateButtonAlpha(
-                            view = button,
-                            viewModel = viewModel,
-                            alphaFlow = alpha,
-                        )
+                        updateButtonAlpha(view = button, viewModel = viewModel, alphaFlow = alpha)
                     }
 
                     launch {
@@ -117,6 +124,32 @@
                             }
                         }
                     }
+
+                    if (Flags.msdlFeedback()) {
+                        launch {
+                            hapticsViewModel
+                                ?.quickAffordanceHapticState
+                                ?.filter {
+                                    it !=
+                                        KeyguardQuickAffordanceHapticViewModel.HapticState
+                                            .NO_HAPTICS
+                                }
+                                ?.collect { state ->
+                                    when (state) {
+                                        KeyguardQuickAffordanceHapticViewModel.HapticState
+                                            .TOGGLE_ON -> msdlPlayer.playToken(MSDLToken.SWITCH_ON)
+                                        KeyguardQuickAffordanceHapticViewModel.HapticState
+                                            .TOGGLE_OFF ->
+                                            msdlPlayer.playToken(MSDLToken.SWITCH_OFF)
+                                        KeyguardQuickAffordanceHapticViewModel.HapticState.LAUNCH ->
+                                            msdlPlayer.playToken(MSDLToken.LONG_PRESS)
+                                        KeyguardQuickAffordanceHapticViewModel.HapticState
+                                            .NO_HAPTICS -> Unit
+                                    }
+                                    hapticsViewModel.resetLaunchingFromTriggeredResult()
+                                }
+                        }
+                    }
                 }
             }
 
@@ -178,7 +211,7 @@
                     com.android.internal.R.color.materialColorOnPrimaryFixed
                 } else {
                     com.android.internal.R.color.materialColorOnSurface
-                },
+                }
             )
         )
 
@@ -221,12 +254,7 @@
                             .getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
                             .toFloat()
                     val shakeAnimator =
-                        ObjectAnimator.ofFloat(
-                            view,
-                            "translationX",
-                            -amplitude / 2,
-                            amplitude / 2,
-                        )
+                        ObjectAnimator.ofFloat(view, "translationX", -amplitude / 2, amplitude / 2)
                     shakeAnimator.duration =
                         KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
                     shakeAnimator.interpolator =
@@ -234,11 +262,17 @@
                     shakeAnimator.doOnEnd { view.translationX = 0f }
                     shakeAnimator.start()
 
-                    vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+                    vibratorHelper?.playFeedback(KeyguardBottomAreaVibrations.Shake, msdlPlayer)
                     logger.logQuickAffordanceTapped(viewModel.configKey)
                 }
                 view.onLongClickListener =
-                    OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
+                    OnLongClickListener(
+                        falsingManager,
+                        viewModel,
+                        vibratorHelper,
+                        onTouchListener,
+                        msdlPlayer,
+                    )
             } else {
                 view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager)))
             }
@@ -268,7 +302,7 @@
                 Size(
                     view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
                     view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
-                ),
+                )
         )
     }
 
@@ -297,7 +331,8 @@
         private val falsingManager: FalsingManager?,
         private val viewModel: KeyguardQuickAffordanceViewModel,
         private val vibratorHelper: VibratorHelper?,
-        private val onTouchListener: KeyguardQuickAffordanceOnTouchListener
+        private val onTouchListener: KeyguardQuickAffordanceOnTouchListener,
+        private val msdlPlayer: MSDLPlayer,
     ) : View.OnLongClickListener {
         override fun onLongClick(view: View): Boolean {
             if (falsingManager?.isFalseLongTap(FalsingManager.MODERATE_PENALTY) == true) {
@@ -312,12 +347,13 @@
                         slotId = viewModel.slotId,
                     )
                 )
-                vibratorHelper?.vibrate(
+                vibratorHelper?.playFeedback(
                     if (viewModel.isActivated) {
                         KeyguardBottomAreaVibrations.Activated
                     } else {
                         KeyguardBottomAreaVibrations.Deactivated
-                    }
+                    },
+                    msdlPlayer,
                 )
             }
 
@@ -328,7 +364,15 @@
         override fun onLongClickUseDefaultHapticFeedback(view: View) = false
     }
 
-    private data class ConfigurationBasedDimensions(
-        val buttonSizePx: Size,
-    )
+    private data class ConfigurationBasedDimensions(val buttonSizePx: Size)
+}
+
+private fun VibratorHelper.playFeedback(effect: VibrationEffect, msdlPlayer: MSDLPlayer) {
+    if (!Flags.msdlFeedback()) {
+        vibrate(effect)
+    } else {
+        if (effect == KeyguardBottomAreaVibrations.Shake) {
+            msdlPlayer.playToken(MSDLToken.FAILURE)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index a2ce4ec..6d270b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -127,21 +127,18 @@
                     if (Flags.nonTouchscreenDevicesBypassFalsing()) {
                         if (
                             event.action == MotionEvent.ACTION_DOWN &&
-                            event.buttonState == MotionEvent.BUTTON_PRIMARY &&
-                            !event.isTouchscreenSource()
+                                event.buttonState == MotionEvent.BUTTON_PRIMARY &&
+                                !event.isTouchscreenSource()
                         ) {
                             consumed = true
                         } else if (
-                            event.action == MotionEvent.ACTION_UP &&
-                            !event.isTouchscreenSource()
+                            event.action == MotionEvent.ACTION_UP && !event.isTouchscreenSource()
                         ) {
                             statusBarKeyguardViewManager?.showBouncer(true)
                             consumed = true
                         }
                     }
-                    viewModel.setRootViewLastTapPosition(
-                        Point(event.x.toInt(), event.y.toInt())
-                    )
+                    viewModel.setRootViewLastTapPosition(Point(event.x.toInt(), event.y.toInt()))
                 }
                 consumed
             }
@@ -172,7 +169,6 @@
                     launch("$TAG#alpha") {
                         viewModel.alpha(viewState).collect { alpha ->
                             view.alpha = alpha
-                            childViews[statusViewId]?.alpha = alpha
                             childViews[burnInLayerId]?.alpha = alpha
                         }
                     }
@@ -253,18 +249,6 @@
                     }
 
                     launch {
-                        viewModel.burnInLayerAlpha.collect { alpha ->
-                            childViews[statusViewId]?.alpha = alpha
-                        }
-                    }
-
-                    launch {
-                        viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
-                            childViews[statusViewId]?.alpha = alpha
-                        }
-                    }
-
-                    launch {
                         viewModel.scale.collect { scaleViewModel ->
                             if (scaleViewModel.scaleClockOnly) {
                                 // For clocks except weather clock, we have scale transition besides
@@ -553,7 +537,6 @@
         return device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == true
     }
 
-    private val statusViewId = R.id.keyguard_status_view
     private val burnInLayerId = R.id.burn_in_layer
     private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
     private val largeClockId = customR.id.lockscreen_clock_view_large
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 090b659..6fb31c0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -48,19 +48,16 @@
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import androidx.core.view.isInvisible
-import com.android.internal.policy.SystemBarUtils
 import com.android.keyguard.ClockEventController
-import com.android.keyguard.KeyguardClockSwitch
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder
 import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel
-import com.android.systemui.coroutines.newTracingContext
+import com.android.systemui.customization.R as customR
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
@@ -80,7 +77,6 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.shared.clocks.DefaultClockController
 import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
 import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
@@ -91,18 +87,13 @@
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 import org.json.JSONException
 import org.json.JSONObject
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.customization.R as customR
 
 /** Renders the preview of the lock screen. */
 class KeyguardPreviewRenderer
@@ -110,7 +101,6 @@
 @AssistedInject
 constructor(
     @Application private val context: Context,
-    @Application applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
     @Main private val mainHandler: Handler,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
@@ -157,8 +147,6 @@
     val surfacePackage: SurfaceControlViewHost.SurfacePackage
         get() = checkNotNull(host.surfacePackage)
 
-    private lateinit var largeClockHostView: FrameLayout
-    private lateinit var smallClockHostView: FrameLayout
     private var smartSpaceView: View? = null
 
     private val disposables = DisposableHandles()
@@ -166,29 +154,18 @@
 
     private val shortcutsBindings = mutableSetOf<KeyguardQuickAffordanceViewBinder.Binding>()
 
-    private val coroutineScope: CoroutineScope
-
     @Style.Type private var themeStyle: Int? = null
 
     init {
-        coroutineScope =
-            CoroutineScope(
-                applicationScope.coroutineContext +
-                    Job() +
-                    newTracingContext("KeyguardPreviewRenderer")
-            )
-        disposables += DisposableHandle { coroutineScope.cancel() }
         clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
-
         quickAffordancesCombinedViewModel.enablePreviewMode(
             initiallySelectedSlotId =
-            bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
-                ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
+                    ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
             shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
         )
-        if (MigrateClocksToBlueprint.isEnabled) {
-            clockViewModel.shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance
-        }
+
+        clockViewModel.shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance
         runBlocking(mainDispatcher) {
             host =
                 SurfaceControlViewHost(
@@ -348,6 +325,7 @@
         smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
     }
 
+    @OptIn(ExperimentalCoroutinesApi::class)
     private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
         val keyguardRootView = KeyguardRootView(previewContext, null)
         rootView.addView(
@@ -358,34 +336,23 @@
             ),
         )
 
-        setUpUdfps(
-            previewContext,
-            if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView,
-        )
+        setUpUdfps(previewContext, keyguardRootView)
 
         setupShortcuts(keyguardRootView)
 
         if (!shouldHideClock) {
             setUpClock(previewContext, rootView)
-            if (MigrateClocksToBlueprint.isEnabled) {
-                KeyguardPreviewClockViewBinder.bind(
-                    keyguardRootView,
-                    clockViewModel,
-                    clockRegistry,
-                    ::updateClockAppearance,
-                    ClockPreviewConfig(
-                        previewContext,
-                        getPreviewShadeLayoutWide(display!!),
-                        SceneContainerFlag.isEnabled,
-                    ),
-                )
-            } else {
-                KeyguardPreviewClockViewBinder.bind(
-                    largeClockHostView,
-                    smallClockHostView,
-                    clockViewModel,
-                )
-            }
+            KeyguardPreviewClockViewBinder.bind(
+                keyguardRootView,
+                clockViewModel,
+                clockRegistry,
+                ::updateClockAppearance,
+                ClockPreviewConfig(
+                    previewContext,
+                    getPreviewShadeLayoutWide(display!!),
+                    SceneContainerFlag.isEnabled,
+                ),
+            )
         }
 
         setUpSmartspace(previewContext, rootView)
@@ -451,82 +418,22 @@
                 .inflate(R.layout.udfps_keyguard_preview, parentView, false) as View
 
         // Place the UDFPS view in the proper sensor location
-        if (MigrateClocksToBlueprint.isEnabled) {
-            val lockId = KeyguardPreviewClockViewBinder.lockId
-            finger.id = lockId
-            parentView.addView(finger)
-            val cs = ConstraintSet()
-            cs.clone(parentView as ConstraintLayout)
-            cs.apply {
-                constrainWidth(lockId, sensorBounds.width())
-                constrainHeight(lockId, sensorBounds.height())
-                connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
-                connect(lockId, START, PARENT_ID, START, sensorBounds.left)
-            }
-            cs.applyTo(parentView)
-        } else {
-            val fingerprintLayoutParams =
-                FrameLayout.LayoutParams(sensorBounds.width(), sensorBounds.height())
-            fingerprintLayoutParams.setMarginsRelative(
-                sensorBounds.left,
-                sensorBounds.top,
-                sensorBounds.right,
-                sensorBounds.bottom,
-            )
-            parentView.addView(finger, fingerprintLayoutParams)
+        val lockId = KeyguardPreviewClockViewBinder.lockId
+        finger.id = lockId
+        parentView.addView(finger)
+        val cs = ConstraintSet()
+        cs.clone(parentView as ConstraintLayout)
+        cs.apply {
+            constrainWidth(lockId, sensorBounds.width())
+            constrainHeight(lockId, sensorBounds.height())
+            connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
+            connect(lockId, START, PARENT_ID, START, sensorBounds.left)
         }
+        cs.applyTo(parentView)
     }
 
     private fun setUpClock(previewContext: Context, parentView: ViewGroup) {
         val resources = parentView.resources
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            largeClockHostView = FrameLayout(previewContext)
-            largeClockHostView.layoutParams =
-                FrameLayout.LayoutParams(
-                    FrameLayout.LayoutParams.MATCH_PARENT,
-                    FrameLayout.LayoutParams.MATCH_PARENT,
-                )
-            largeClockHostView.isInvisible = true
-            parentView.addView(largeClockHostView)
-
-            smallClockHostView = FrameLayout(previewContext)
-            val layoutParams =
-                FrameLayout.LayoutParams(
-                    FrameLayout.LayoutParams.WRAP_CONTENT,
-                    resources.getDimensionPixelSize(customR.dimen.small_clock_height),
-                )
-            layoutParams.topMargin =
-                SystemBarUtils.getStatusBarHeight(previewContext) +
-                    resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top)
-            smallClockHostView.layoutParams = layoutParams
-            smallClockHostView.setPaddingRelative(
-                /* start = */ resources.getDimensionPixelSize(customR.dimen.clock_padding_start),
-                /* top = */ 0,
-                /* end = */ 0,
-                /* bottom = */ 0,
-            )
-            smallClockHostView.clipChildren = false
-            parentView.addView(smallClockHostView)
-            smallClockHostView.isInvisible = true
-        }
-
-        // TODO (b/283465254): Move the listeners to KeyguardClockRepository
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            val clockChangeListener =
-                object : ClockRegistry.ClockChangeListener {
-                    override fun onCurrentClockChanged() {
-                        onClockChanged()
-                    }
-                }
-            clockRegistry.registerClockChangeListener(clockChangeListener)
-            disposables += DisposableHandle {
-                clockRegistry.unregisterClockChangeListener(clockChangeListener)
-            }
-
-            clockController.registerListeners(parentView)
-            disposables += DisposableHandle { clockController.unregisterListeners() }
-        }
-
         val receiver =
             object : BroadcastReceiver() {
                 override fun onReceive(context: Context?, intent: Intent?) {
@@ -544,38 +451,9 @@
             },
         )
         disposables += DisposableHandle { broadcastDispatcher.unregisterReceiver(receiver) }
-
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            val layoutChangeListener =
-                View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
-                    if (clockController.clock !is DefaultClockController) {
-                        clockController.clock
-                            ?.largeClock
-                            ?.events
-                            ?.onTargetRegionChanged(
-                                KeyguardClockSwitch.getLargeClockRegion(parentView)
-                            )
-                        clockController.clock
-                            ?.smallClock
-                            ?.events
-                            ?.onTargetRegionChanged(
-                                KeyguardClockSwitch.getSmallClockRegion(parentView)
-                            )
-                    }
-                }
-            parentView.addOnLayoutChangeListener(layoutChangeListener)
-            disposables += DisposableHandle {
-                parentView.removeOnLayoutChangeListener(layoutChangeListener)
-            }
-        }
-
-        onClockChanged()
     }
 
     private suspend fun updateClockAppearance(clock: ClockController, resources: Resources) {
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            clockController.clock = clock
-        }
         val colors = wallpaperColors
         if (clockRegistry.seedColor == null && colors != null) {
             // Seed color null means users do not override any color on the clock. The default
@@ -601,9 +479,7 @@
         // In clock preview, we should have a seed color for clock
         // before setting clock to clockEventController to avoid updateColor with seedColor == null
         // So in update colors, it should already have the correct theme in clockFaceController
-        if (MigrateClocksToBlueprint.isEnabled) {
-            clockController.clock = clock
-        }
+        clockController.clock = clock
         // When set clock to clockController,it will reset fontsize based on context.resources
         // We need to override it with overlaid resources
         clock.largeClock.events.onFontSettingChanged(
@@ -611,19 +487,6 @@
         )
     }
 
-    private fun onClockChanged() {
-        if (MigrateClocksToBlueprint.isEnabled) {
-            return
-        }
-        coroutineScope.launch {
-            val clock = clockRegistry.createCurrentClock()
-            clockController.clock = clock
-            updateClockAppearance(clock, context.resources)
-            updateLargeClock(clock)
-            updateSmallClock(clock)
-        }
-    }
-
     private fun setupCommunalTutorialIndicator(keyguardRootView: ConstraintLayout) {
         keyguardRootView.findViewById<TextView>(R.id.communal_tutorial_indicator)?.let {
             indicatorView ->
@@ -657,34 +520,6 @@
         }
     }
 
-    private fun updateLargeClock(clock: ClockController) {
-        if (MigrateClocksToBlueprint.isEnabled) {
-            return
-        }
-        clock.largeClock.events.onTargetRegionChanged(
-            KeyguardClockSwitch.getLargeClockRegion(largeClockHostView)
-        )
-        if (shouldHighlightSelectedAffordance) {
-            clock.largeClock.view.alpha = DIM_ALPHA
-        }
-        largeClockHostView.removeAllViews()
-        largeClockHostView.addView(clock.largeClock.view)
-    }
-
-    private fun updateSmallClock(clock: ClockController) {
-        if (MigrateClocksToBlueprint.isEnabled) {
-            return
-        }
-        clock.smallClock.events.onTargetRegionChanged(
-            KeyguardClockSwitch.getSmallClockRegion(smallClockHostView)
-        )
-        if (shouldHighlightSelectedAffordance) {
-            clock.smallClock.view.alpha = DIM_ALPHA
-        }
-        smallClockHostView.removeAllViews()
-        smallClockHostView.addView(clock.smallClock.view)
-    }
-
     private fun getPreviewShadeLayoutWide(display: Display): Boolean {
         return if (display.displayId == 0) {
             shadeInteractor.isShadeLayoutWide.value
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index 729759a..5d463f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -23,7 +23,6 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationPanelView
 import com.android.systemui.shade.ShadeDisplayAware
@@ -50,16 +49,13 @@
         sharedNotificationContainerBinder,
     ) {
     override fun applyConstraints(constraintSet: ConstraintSet) {
-        if (!MigrateClocksToBlueprint.isEnabled) {
-            return
-        }
         constraintSet.apply {
             connect(
                 R.id.nssl_placeholder,
                 TOP,
                 PARENT_ID,
                 TOP,
-                context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
+                context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin),
             )
             connect(R.id.nssl_placeholder, START, PARENT_ID, START)
             connect(R.id.nssl_placeholder, END, PARENT_ID, END)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 1c89723..fb311a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -194,12 +193,7 @@
                 (!useAltAod) && keyguardClockViewModel.clockSize.value == ClockSize.LARGE
 
             val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
-            val translationY =
-                if (MigrateClocksToBlueprint.isEnabled) {
-                    max(params.topInset - params.minViewY, burnInY)
-                } else {
-                    max(params.topInset, params.minViewY + burnInY) - params.minViewY
-                }
+            val translationY = max(params.topInset - params.minViewY, burnInY)
             BurnInModel(
                 translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
                 translationY = translationY,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
new file mode 100644
index 0000000..890628c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+class KeyguardQuickAffordanceHapticViewModel
+@AssistedInject
+constructor(
+    @Assisted quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>,
+    private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
+) {
+
+    private val activatedHistory = MutableStateFlow(ActivatedHistory(false))
+
+    private val launchingHapticState: Flow<HapticState> =
+        combine(
+                quickAffordanceViewModel.map { it.configKey },
+                quickAffordanceInteractor.launchingFromTriggeredResult,
+            ) { key, launchingResult ->
+                val validKey = key != null && key == launchingResult?.configKey
+                if (validKey && launchingResult?.launched == true) {
+                    HapticState.LAUNCH
+                } else {
+                    HapticState.NO_HAPTICS
+                }
+            }
+            .distinctUntilChanged()
+
+    private val toggleHapticState: Flow<HapticState> =
+        activatedHistory
+            .map { history ->
+                when {
+                    history.previousValue == false && history.currentValue -> HapticState.TOGGLE_ON
+                    history.previousValue == true && !history.currentValue -> HapticState.TOGGLE_OFF
+                    else -> HapticState.NO_HAPTICS
+                }
+            }
+            .distinctUntilChanged()
+
+    val quickAffordanceHapticState =
+        merge(launchingHapticState, toggleHapticState).distinctUntilChanged()
+
+    fun resetLaunchingFromTriggeredResult() =
+        quickAffordanceInteractor.setLaunchingFromTriggeredResult(null)
+
+    fun updateActivatedHistory(isActivated: Boolean) {
+        activatedHistory.value =
+            ActivatedHistory(
+                currentValue = isActivated,
+                previousValue = activatedHistory.value.currentValue,
+            )
+    }
+
+    enum class HapticState {
+        TOGGLE_ON,
+        TOGGLE_OFF,
+        LAUNCH,
+        NO_HAPTICS,
+    }
+
+    private data class ActivatedHistory(
+        val currentValue: Boolean,
+        val previousValue: Boolean? = null,
+    )
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>
+        ): KeyguardQuickAffordanceHapticViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 9066d46..eaba5d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -130,7 +129,6 @@
         PrimaryBouncerToLockscreenTransitionViewModel,
     private val screenOffAnimationController: ScreenOffAnimationController,
     private val aodBurnInViewModel: AodBurnInViewModel,
-    private val aodAlphaViewModel: AodAlphaViewModel,
     private val shadeInteractor: ShadeInteractor,
 ) {
     val burnInLayerVisibility: Flow<Int> =
@@ -284,15 +282,6 @@
             .distinctUntilChanged()
     }
 
-    /** Specific alpha value for elements visible during [KeyguardState.LOCKSCREEN] */
-    @Deprecated("only used for legacy status view")
-    fun lockscreenStateAlpha(viewState: ViewStateAccessor): Flow<Float> {
-        return aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState)
-    }
-
-    /** For elements that appear and move during the animation -> AOD */
-    val burnInLayerAlpha: Flow<Float> = aodAlphaViewModel.alpha
-
     val translationY: Flow<Float> = aodBurnInViewModel.movement.map { it.translationY.toFloat() }
 
     val translationX: Flow<StateToValue> =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index c32bd40..b4dabbe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -34,13 +34,14 @@
 import android.view.ViewGroupOverlay
 import androidx.annotation.VisibleForTesting
 import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.app.tracing.traceSection
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix
 import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dreams.DreamOverlayStateController
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -68,7 +69,6 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 private val TAG: String = MediaHierarchyManager::class.java.simpleName
 
@@ -115,7 +115,7 @@
     wakefulnessLifecycle: WakefulnessLifecycle,
     shadeInteractor: ShadeInteractor,
     private val secureSettings: SecureSettings,
-    @Main private val handler: Handler,
+    @Background private val handler: Handler,
     @Application private val coroutineScope: CoroutineScope,
     private val splitShadeStateController: SplitShadeStateController,
     private val logger: MediaViewLogger,
@@ -631,7 +631,7 @@
                     }
                 }
             }
-        secureSettings.registerContentObserverForUserSync(
+        secureSettings.registerContentObserverForUserAsync(
             Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
             settingsObserver,
             UserHandle.USER_ALL,
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 311cbfb..b2696aea 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -132,7 +132,7 @@
         val isDefaultNotesAppSet =
             noteTaskInfoResolver.resolveInfo(
                 QUICK_AFFORDANCE,
-                user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+                user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE),
             ) != null
         return when {
             isEnabled && isDefaultNotesAppSet -> PickerScreenState.Default()
@@ -158,7 +158,7 @@
 
     override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
         controller.showNoteTask(entryPoint = QUICK_AFFORDANCE)
-        return OnTriggeredResult.Handled
+        return OnTriggeredResult.Handled(true)
     }
 }
 
@@ -194,7 +194,7 @@
     fun isDefaultNotesAppSetForUser() =
         noteTaskInfoResolver.resolveInfo(
             QUICK_AFFORDANCE,
-            user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+            user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE),
         ) != null
 
     trySendBlocking(isDefaultNotesAppSetForUser())
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt b/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt
index ef7e7eb..84b995e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt
@@ -22,16 +22,21 @@
 
 /**
  * Creates a [QSTile.Icon] from an [Icon].
- * * [Icon.Loaded] -> [QSTileImpl.DrawableIcon]
+ * * [Icon.Loaded] with null [res] -> [QSTileImpl.DrawableIcon]
+ * * [Icon.Loaded] & with non null [res] -> [QSTileImpl.DrawableIconWithRes]
  * * [Icon.Resource] -> [QSTileImpl.ResourceIcon]
  */
 fun Icon.asQSTileIcon(): QSTile.Icon {
     return when (this) {
         is Icon.Loaded -> {
-            QSTileImpl.DrawableIcon(this.drawable)
+            if (res == null) {
+                QSTileImpl.DrawableIcon(drawable)
+            } else {
+                QSTileImpl.DrawableIconWithRes(drawable, res)
+            }
         }
         is Icon.Resource -> {
-            QSTileImpl.ResourceIcon.get(this.res)
+            QSTileImpl.ResourceIcon.get(res)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 9b2880b..ee6b0b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -79,7 +79,10 @@
         } else {
             return ModesTileModel(
                 isActivated = activeModes.isAnyActive(),
-                icon = context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(),
+                icon =
+                    context
+                        .getDrawable(ModesTile.ICON_RES_ID)!!
+                        .asIcon(res = ModesTile.ICON_RES_ID),
                 iconResId = ModesTile.ICON_RES_ID,
                 activeModes = activeModes.modeNames,
             )
@@ -92,12 +95,18 @@
         return if (activeMode != null) {
             // ZenIconKey.resPackage is null if its resId is a system icon.
             if (activeMode.icon.key.resPackage == null) {
-                TileIcon(activeMode.icon.drawable.asIcon(), activeMode.icon.key.resId)
+                TileIcon(
+                    activeMode.icon.drawable.asIcon(res = activeMode.icon.key.resId),
+                    activeMode.icon.key.resId,
+                )
             } else {
                 TileIcon(activeMode.icon.drawable.asIcon(), null)
             }
         } else {
-            TileIcon(context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(), ModesTile.ICON_RES_ID)
+            TileIcon(
+                context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(res = ModesTile.ICON_RES_ID),
+                ModesTile.ICON_RES_ID,
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 66af275..a7dbb47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.chips.notification.ui.viewmodel
 
 import android.view.View
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
@@ -99,6 +100,17 @@
             )
         }
 
+        if (Flags.promoteNotificationsAutomatically()) {
+            // When we're promoting notifications automatically, the `when` time set on the
+            // notification will likely just be set to the current time, which would cause the chip
+            // to always show "now". We don't want early testers to get that experience since it's
+            // not what will happen at launch, so just don't show any time.
+            // TODO(b/364653005): Only ignore the `when` time if the notification was
+            //  *automatically* promoted (as opposed to being legitimately promoted by the
+            // criteria). We'll need to track that status somehow.
+            return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+        }
+
         if (this.promotedContent.time == null) {
             return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
new file mode 100644
index 0000000..9f523fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the status bar popup chips flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarPopupChips {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_POPUP_CHIPS
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.statusBarPopupChips()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is not enabled to ensure that the refactor author catches issues in testing.
+     * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+     */
+    @JvmStatic
+    inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
new file mode 100644
index 0000000..1663aeb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.shared.model
+
+import com.android.systemui.common.shared.model.Icon
+
+/**
+ * Ids used to track different types of popup chips. Will be used to ensure only one chip is
+ * displaying its popup at a time.
+ */
+sealed class PopupChipId(val value: String) {
+    data object MediaControls : PopupChipId("MediaControls")
+}
+
+/** Model for individual status bar popup chips. */
+sealed class PopupChipModel {
+    abstract val logName: String
+    abstract val chipId: PopupChipId
+
+    data class Hidden(override val chipId: PopupChipId, val shouldAnimate: Boolean = true) :
+        PopupChipModel() {
+        override val logName = "Hidden(id=$chipId, anim=$shouldAnimate)"
+    }
+
+    data class Shown(
+        override val chipId: PopupChipId,
+        val icon: Icon,
+        val chipText: String,
+        val isToggled: Boolean = false,
+        val onToggle: () -> Unit,
+        val onIconPressed: () -> Unit,
+    ) : PopupChipModel() {
+        override val logName = "Shown(id=$chipId, toggled=$isToggled)"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
new file mode 100644
index 0000000..5712be3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
+
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface for a view model that knows the display requirements for a single type of status bar
+ * popup chip.
+ */
+interface StatusBarPopupChipViewModel {
+    /** A flow modeling the popup chip that should be shown (or not shown). */
+    val chip: StateFlow<PopupChipModel>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
new file mode 100644
index 0000000..b390f29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * View model deciding which system process chips to show in the status bar. Emits a list of
+ * PopupChipModels.
+ */
+@SysUISingleton
+class StatusBarPopupChipsViewModel @Inject constructor(@Background scope: CoroutineScope) {
+    private data class PopupChipBundle(
+        val media: PopupChipModel = PopupChipModel.Hidden(chipId = PopupChipId.MediaControls)
+    )
+
+    private val incomingPopupChipBundle: Flow<PopupChipBundle?> =
+        flowOf(null).stateIn(scope, SharingStarted.Lazily, PopupChipBundle())
+
+    val popupChips: Flow<List<PopupChipModel>> =
+        incomingPopupChipBundle
+            .map { _ -> listOf(null).filterIsInstance<PopupChipModel.Shown>() }
+            .stateIn(scope, SharingStarted.Lazily, emptyList())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
index 13ad141..a43f8db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel.ERROR
 import com.android.systemui.log.core.LogLevel.INFO
-import com.android.systemui.log.dagger.NotificationLog
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.logKey
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
@@ -27,7 +26,7 @@
 
 class PromotedNotificationLogger
 @Inject
-constructor(@NotificationLog private val buffer: LogBuffer) {
+constructor(@PromotedNotificationLog private val buffer: LogBuffer) {
     fun logExtractionSkipped(entry: NotificationEntry, reason: String) {
         buffer.log(
             EXTRACTION_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
new file mode 100644
index 0000000..0f21514
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class AODPromotedNotificationInteractor
+@Inject
+constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) {
+    val content: Flow<PromotedNotificationContentModel?> =
+        activeNotificationsInteractor.topLevelRepresentativeNotifications.map { notifs ->
+            notifs.firstNotNullOfOrNull { it.promotedContent }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index fe2dabe..74809fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -28,7 +28,7 @@
  * like the skeleton view on AOD or the status bar chip.
  */
 data class PromotedNotificationContentModel(
-    val key: String,
+    val identity: Identity,
 
     // for all styles:
     val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
@@ -82,7 +82,7 @@
 
         fun build() =
             PromotedNotificationContentModel(
-                key = key,
+                identity = Identity(key, style),
                 skeletonSmallIcon = skeletonSmallIcon,
                 appName = appName,
                 subText = subText,
@@ -103,6 +103,8 @@
             )
     }
 
+    data class Identity(val key: String, val style: Style)
+
     /** The timestamp associated with a notification, along with the mode used to display it. */
     data class When(val time: Long, val mode: Mode) {
         /** The mode used to display a notification's `when` value. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt
new file mode 100644
index 0000000..adfa6a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.AODPromotedNotificationInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Identity
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class AODPromotedNotificationViewModel
+@Inject
+constructor(interactor: AODPromotedNotificationInteractor) {
+    private val content: Flow<PromotedNotificationContentModel?> = interactor.content
+    private val identity: Flow<Identity?> = content.mapNonNullsKeepingNulls { it.identity }
+
+    val notification: Flow<PromotedNotificationViewModel?> =
+        identity.distinctUntilChanged().mapNonNullsKeepingNulls { identity ->
+            val updates = interactor.content.filterNotNull().filter { it.identity == identity }
+            PromotedNotificationViewModel(identity, updates)
+        }
+}
+
+private fun <T, R> Flow<T?>.mapNonNullsKeepingNulls(block: (T) -> R): Flow<R?> = map {
+    it?.let(block)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt
new file mode 100644
index 0000000..f265e0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.ui.viewmodel
+
+import android.graphics.drawable.Icon
+import com.android.internal.widget.NotificationProgressModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class PromotedNotificationViewModel(
+    identity: PromotedNotificationContentModel.Identity,
+    content: Flow<PromotedNotificationContentModel>,
+) {
+    // for all styles:
+
+    val key: String = identity.key
+    val style: Style = identity.style
+
+    val skeletonSmallIcon: Flow<Icon?> = content.map { it.skeletonSmallIcon }
+    val appName: Flow<CharSequence?> = content.map { it.appName }
+    val subText: Flow<CharSequence?> = content.map { it.subText }
+
+    private val time: Flow<When?> = content.map { it.time }
+    val whenTime: Flow<Long?> = time.map { it?.time }
+    val whenMode: Flow<When.Mode?> = time.map { it?.mode }
+
+    val lastAudiblyAlertedMs: Flow<Long> = content.map { it.lastAudiblyAlertedMs }
+    val profileBadgeResId: Flow<Int?> = content.map { it.profileBadgeResId }
+    val title: Flow<CharSequence?> = content.map { it.title }
+    val text: Flow<CharSequence?> = content.map { it.text }
+    val skeletonLargeIcon: Flow<Icon?> = content.map { it.skeletonLargeIcon }
+
+    // for CallStyle:
+    val personIcon: Flow<Icon?> = content.map { it.personIcon }
+    val personName: Flow<CharSequence?> = content.map { it.personName }
+    val verificationIcon: Flow<Icon?> = content.map { it.verificationIcon }
+    val verificationText: Flow<CharSequence?> = content.map { it.verificationText }
+
+    // for ProgressStyle:
+    val progress: Flow<NotificationProgressModel?> = content.map { it.progress }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
index 42acd7bc..705845f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
@@ -75,7 +75,7 @@
         constraintSet.apply {
             if (SceneContainerFlag.isEnabled) {
                 when (horizontalPosition) {
-                    is HorizontalPosition.FloatAtEnd ->
+                    is HorizontalPosition.FloatAtStart ->
                         constrainWidth(nsslId, horizontalPosition.width)
                     is HorizontalPosition.MiddleToEdge ->
                         setGuidelinePercent(R.id.nssl_guideline, horizontalPosition.ratio)
@@ -83,13 +83,13 @@
                 }
             }
 
+            connect(nsslId, START, startConstraintId, START, marginStart)
             if (
                 !SceneContainerFlag.isEnabled ||
-                    horizontalPosition !is HorizontalPosition.FloatAtEnd
+                    horizontalPosition !is HorizontalPosition.FloatAtStart
             ) {
-                connect(nsslId, START, startConstraintId, START, marginStart)
+                connect(nsslId, END, PARENT_ID, END, marginEnd)
             }
-            connect(nsslId, END, PARENT_ID, END, marginEnd)
             connect(nsslId, BOTTOM, PARENT_ID, BOTTOM, marginBottom)
             connect(nsslId, TOP, PARENT_ID, TOP, marginTop)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index b81c71e..fc8c70f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -247,7 +247,7 @@
                                 Split -> HorizontalPosition.MiddleToEdge(ratio = 0.5f)
                                 Dual ->
                                     if (isShadeLayoutWide) {
-                                        HorizontalPosition.FloatAtEnd(
+                                        HorizontalPosition.FloatAtStart(
                                             width = getDimensionPixelSize(R.dimen.shade_panel_width)
                                         )
                                     } else {
@@ -830,10 +830,10 @@
         data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition
 
         /**
-         * The container has a fixed [width] and is aligned to the end of the screen. In this
-         * layout, the start edge of the container is floating, i.e. unconstrained.
+         * The container has a fixed [width] and is aligned to the start of the screen. In this
+         * layout, the end edge of the container is floating, i.e. unconstrained.
          */
-        data class FloatAtEnd(val width: Int) : HorizontalPosition
+        data class FloatAtStart(val width: Int) : HorizontalPosition
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index a382cf9..e08114f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -21,10 +21,8 @@
 import android.hardware.devicestate.DeviceStateManager
 import android.os.PowerManager
 import android.provider.Settings
-import androidx.core.view.OneShotPreDrawListener
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.ToAodFoldTransitionInteractor
@@ -125,11 +123,7 @@
 
     private val shadeFoldAnimator: ShadeFoldAnimator
         get() {
-            return if (MigrateClocksToBlueprint.isEnabled) {
-                foldTransitionInteractor.get().foldAnimator
-            } else {
-                shadeViewController.shadeFoldAnimator
-            }
+            return foldTransitionInteractor.get().foldAnimator
         }
 
     private fun setAnimationState(playing: Boolean) {
@@ -164,15 +158,7 @@
                 setAnimationState(playing = true)
                 shadeFoldAnimator.prepareFoldToAodAnimation()
 
-                // We don't need to wait for the scrim as it is already displayed
-                // but we should wait for the initial animation preparations to be drawn
-                // (setting initial alpha/translation)
-                // TODO(b/254878364): remove this call to NPVC.getView()
-                if (!MigrateClocksToBlueprint.isEnabled) {
-                    shadeFoldAnimator.view?.let { OneShotPreDrawListener.add(it, onReady) }
-                } else {
-                    onReady.run()
-                }
+                onReady.run()
             } else {
                 // No animation, call ready callback immediately
                 onReady.run()
@@ -252,7 +238,7 @@
                 if (isFolded) {
                     foldToAodLatencyTracker.onFolded()
                 }
-            }
+            },
         )
 
     /**
@@ -272,6 +258,7 @@
                 latencyTracker.onActionStart(LatencyTracker.ACTION_FOLD_TO_AOD)
             }
         }
+
         /**
          * Called once the Fold -> AOD animation is started.
          *
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 32fa160..21dd5bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -344,7 +344,7 @@
                         canShowWhileLocked = canShowWhileLocked,
                     )
                 } else {
-                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                 }
 
             underTest.onQuickAffordanceTriggered(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index eb19a9c..1ce128c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -347,7 +347,7 @@
                         canShowWhileLocked = canShowWhileLocked,
                     )
                 } else {
-                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
                 }
 
             underTest.onQuickAffordanceTriggered(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt
new file mode 100644
index 0000000..57c8fd0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+
+val Kosmos.communalBackActionInteractor by
+    Kosmos.Fixture {
+        CommunalBackActionInteractor(
+            communalInteractor = communalInteractor,
+            communalSceneInteractor = communalSceneInteractor,
+            sceneInteractor = sceneInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index 548b564..5d20669 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -30,7 +30,7 @@
     override val pickerIconResourceId: Int = 0,
 ) : KeyguardQuickAffordanceConfig {
 
-    var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled
+    var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled(false)
 
     private val _lockScreenState =
         MutableStateFlow<KeyguardQuickAffordanceConfig.LockScreenState>(
@@ -41,9 +41,7 @@
 
     override fun pickerName(): String = pickerName
 
-    override fun onTriggered(
-        expandable: Expandable?,
-    ): OnTriggeredResult {
+    override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
         return onTriggeredResult
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt
new file mode 100644
index 0000000..d857157
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.flow.Flow
+
+val Kosmos.keyguardQuickAffordanceHapticViewModelFactory by
+    Kosmos.Fixture {
+        object : KeyguardQuickAffordanceHapticViewModel.Factory {
+            override fun create(
+                quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>
+            ): KeyguardQuickAffordanceHapticViewModel =
+                KeyguardQuickAffordanceHapticViewModel(
+                    quickAffordanceViewModel,
+                    keyguardQuickAffordanceInteractor,
+                )
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index e473107..abbfa93 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -87,7 +87,6 @@
             primaryBouncerToLockscreenTransitionViewModel,
         screenOffAnimationController = screenOffAnimationController,
         aodBurnInViewModel = aodBurnInViewModel,
-        aodAlphaViewModel = aodAlphaViewModel,
         shadeInteractor = shadeInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
new file mode 100644
index 0000000..62cdc87
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+
+val Kosmos.statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel by
+    Kosmos.Fixture { StatusBarPopupChipsViewModel(testScope.backgroundScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
index a357275..f31697e8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
@@ -70,6 +70,14 @@
         }
     }
 
+    fun getContentObservers(uri: Uri, userHandle: Int): List<ContentObserver> {
+        if (userHandle == UserHandle.USER_ALL) {
+            return contentObserversAllUsers[uri.toString()] ?: listOf()
+        } else {
+            return contentObservers[SettingsKey(userHandle, uri.toString())] ?: listOf()
+        }
+    }
+
     override fun getContentResolver(): ContentResolver {
         throw UnsupportedOperationException("FakeSettings.getContentResolver is not implemented")
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 3441d94..9ceca5d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1589,7 +1589,13 @@
      * lock because this calls out to WindowManagerService.
      */
     void addWindowTokensForAllDisplays() {
-        final Display[] displays = mDisplayManager.getDisplays();
+        Display[] displays = {};
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            displays = mDisplayManager.getDisplays();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
         for (int i = 0; i < displays.length; i++) {
             final int displayId = displays[i].getDisplayId();
             addWindowTokenForDisplay(displayId);
@@ -1625,7 +1631,13 @@
     }
 
     public void onRemoved() {
-        final Display[] displays = mDisplayManager.getDisplays();
+        Display[] displays = {};
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            displays = mDisplayManager.getDisplays();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
         for (int i = 0; i < displays.length; i++) {
             final int displayId = displays[i].getDisplayId();
             onDisplayRemoved(displayId);
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java
new file mode 100644
index 0000000..6cab60c
--- /dev/null
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.provider.proxy;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.provider.GnssAssistanceProviderBase;
+import android.location.provider.IGnssAssistanceCallback;
+import android.location.provider.IGnssAssistanceProvider;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.servicewatcher.CurrentUserServiceSupplier;
+import com.android.server.servicewatcher.ServiceWatcher;
+
+/**
+ * Proxy for IGnssAssitanceProvider implementations.
+ */
+public class ProxyGnssAssistanceProvider {
+
+    private static final String TAG = "GnssAssistanceProxy";
+    /**
+     * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+     * null.
+     */
+    @Nullable
+    public static ProxyGnssAssistanceProvider createAndRegister(Context context) {
+        ProxyGnssAssistanceProvider proxy = new ProxyGnssAssistanceProvider(context);
+        if (proxy.register()) {
+            return proxy;
+        } else {
+            return null;
+        }
+    }
+
+    private final ServiceWatcher mServiceWatcher;
+
+    private ProxyGnssAssistanceProvider(Context context) {
+        mServiceWatcher =
+                ServiceWatcher.create(
+                        context,
+                        TAG,
+                        CurrentUserServiceSupplier.createFromConfig(
+                                context,
+                                GnssAssistanceProviderBase.ACTION_GNSS_ASSISTANCE_PROVIDER,
+                                com.android.internal.R.bool.config_enableGnssAssistanceOverlay,
+                                com.android.internal.R.string
+                                        .config_gnssAssistanceProviderPackageName),
+                        /* serviceListener= */ null);
+    }
+
+    private boolean register() {
+        boolean resolves = mServiceWatcher.checkServiceResolves();
+        if (resolves) {
+            mServiceWatcher.register();
+        }
+        return resolves;
+    }
+
+    /**
+     * Request GNSS assistance.
+     */
+    public void request(IGnssAssistanceCallback callback) {
+        mServiceWatcher.runOnBinder(
+                new ServiceWatcher.BinderOperation() {
+                    @Override
+                    public void run(IBinder binder) throws RemoteException {
+                        IGnssAssistanceProvider.Stub.asInterface(binder).request(callback);
+                    }
+
+                    @Override
+                    public void onError(Throwable t) {
+                        try {
+                            Log.w(TAG, "Error on requesting GnssAssistance: " + t);
+                            callback.onError();
+                        } catch (RemoteException e) {
+                            // ignore
+                        }
+                    }
+                });
+    }
+}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 271c818..7f88e74 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -479,6 +479,7 @@
             case PowerManager.PARTIAL_WAKE_LOCK:
                 return BatteryStats.WAKE_TYPE_PARTIAL;
 
+            case PowerManager.FULL_WAKE_LOCK:
             case PowerManager.SCREEN_DIM_WAKE_LOCK:
             case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                 return BatteryStats.WAKE_TYPE_FULL;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 60130d1..30a967c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -91,6 +91,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
+import android.tracing.perfetto.InitArguments;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.Dumpable;
@@ -792,6 +793,12 @@
     private void run() {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         try {
+            if (android.tracing.Flags.systemServerLargePerfettoShmemBuffer()) {
+                // Explicitly initialize a 4 MB shmem buffer for Perfetto producers (b/382369925)
+                android.tracing.perfetto.Producer.init(new InitArguments(
+                        InitArguments.PERFETTO_BACKEND_SYSTEM, 4 * 1024));
+            }
+
             t.traceBegin("InitBeforeStartServices");
 
             // Record the process start information in sys props.
diff --git a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
index 2818379..860d9f6 100644
--- a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
+++ b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
@@ -16,10 +16,17 @@
 
 package com.android.test.input
 
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+
 import android.view.KeyCharacterMap
 import android.view.KeyEvent
 
+import com.android.hardware.input.Flags
+
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Rule
 import org.junit.Test
 
 /**
@@ -30,26 +37,38 @@
  *
  */
 class KeyCharacterMapTest {
+    @get:Rule
+    val setFlagsRule = SetFlagsRule()
+
     @Test
+    @EnableFlags(Flags.FLAG_REMOVE_FALLBACK_MODIFIERS)
     fun testGetFallback() {
         // Based off of VIRTUAL kcm fallbacks.
         val keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
 
         // One modifier fallback.
-        assertEquals(
-            keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
-                KeyEvent.META_CTRL_ON).keyCode,
-            KeyEvent.KEYCODE_LANGUAGE_SWITCH)
+        val oneModifierFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
+            KeyEvent.META_CTRL_ON)
+        assertEquals(KeyEvent.KEYCODE_LANGUAGE_SWITCH, oneModifierFallback.keyCode)
+        assertEquals(0, oneModifierFallback.metaState)
 
         // Multiple modifier fallback.
-        assertEquals(
-            keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
-                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON).keyCode,
-            KeyEvent.KEYCODE_BACK)
+        val twoModifierFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
+            KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON)
+        assertEquals(KeyEvent.KEYCODE_BACK, twoModifierFallback.keyCode)
+        assertEquals(0, twoModifierFallback.metaState)
 
         // No default button, fallback only.
-        assertEquals(
-            keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0).keyCode,
-            KeyEvent.KEYCODE_DPAD_CENTER)
+        val keyOnlyFallback =
+            keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0)
+        assertEquals(KeyEvent.KEYCODE_DPAD_CENTER, keyOnlyFallback.keyCode)
+        assertEquals(0, keyOnlyFallback.metaState)
+
+        // A key event that is not an exact match for a fallback. Expect a null return.
+        // E.g. Ctrl + Space -> LanguageSwitch
+        //      Ctrl + Alt + Space -> Ctrl + Alt + Space (No fallback).
+        val noMatchFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
+            KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON)
+        assertNull(noMatchFallback)
     }
 }
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 661df4d..e24fe07 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -683,8 +683,6 @@
       item->PrettyPrint(printer_);
       printer_->Print(")");
     }
-
-    printer_->Print("\n");
   }
 
   void PrintQualifiers(uint32_t qualifiers) const {
@@ -763,11 +761,13 @@
 
   bool PrintTableType(const ResTable_type* chunk) {
     printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id)));
-    printer_->Print(StringPrintf(
-        " name: %s",
-        android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1)
-            .c_str()));
+    const auto name =
+        android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1);
+    printer_->Print(StringPrintf(" name: %s", name.c_str()));
     printer_->Print(StringPrintf(" flags: 0x%02x", android::util::DeviceToHost32(chunk->flags)));
+    printer_->Print(android::util::DeviceToHost32(chunk->flags) & ResTable_type::FLAG_SPARSE
+                        ? " (SPARSE)"
+                        : " (DENSE)");
     printer_->Print(
         StringPrintf(" entryCount: %u", android::util::DeviceToHost32(chunk->entryCount)));
     printer_->Print(
@@ -777,8 +777,7 @@
     config.copyFromDtoH(chunk->config);
     printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str()));
 
-    const ResourceType* type = ParseResourceType(
-        android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1));
+    const ResourceType* type = ParseResourceType(name);
 
     printer_->Indent();
 
@@ -817,11 +816,8 @@
         for (size_t i = 0; i < map_entry_count; i++) {
           PrintResValue(&(maps[i].value), config, type);
 
-          printer_->Print(StringPrintf(
-              " name: %s name-id:%d\n",
-              android::util::GetString(key_pool_, android::util::DeviceToHost32(maps[i].name.ident))
-                  .c_str(),
-              android::util::DeviceToHost32(maps[i].name.ident)));
+          printer_->Print(StringPrintf(" name-id: 0x%08x\n",
+                                       android::util::DeviceToHost32(maps[i].name.ident)));
         }
       } else {
         printer_->Print("\n");
@@ -829,6 +825,8 @@
         // Print the value of the entry
         Res_value value = entry->value();
         PrintResValue(&value, config, type);
+
+        printer_->Print("\n");
       }
 
       printer_->Undent();