Merge "Population Density Provider: add cache hint" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 3c2ae5a..371177c 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -1220,6 +1220,17 @@
 }
 
 java_aconfig_library {
+    name: "device_policy_aconfig_flags_java_export",
+    aconfig_declarations: "device_policy_aconfig_flags",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    min_sdk_version: "30",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.permission",
+    ],
+}
+
+java_aconfig_library {
     name: "device_policy_aconfig_flags_lib_host",
     aconfig_declarations: "device_policy_aconfig_flags",
     host_supported: true,
diff --git a/core/api/current.txt b/core/api/current.txt
index 1b42977..2fa22d5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17082,7 +17082,7 @@
     method public void arcTo(@NonNull android.graphics.RectF, float, float);
     method public void arcTo(float, float, float, float, float, float, boolean);
     method public void close();
-    method @Deprecated public void computeBounds(@NonNull android.graphics.RectF, boolean);
+    method public void computeBounds(@NonNull android.graphics.RectF, boolean);
     method @FlaggedApi("com.android.graphics.flags.exact_compute_bounds") public void computeBounds(@NonNull android.graphics.RectF);
     method public void conicTo(float, float, float, float, float);
     method public void cubicTo(float, float, float, float, float, float);
@@ -40810,13 +40810,14 @@
 
   public abstract class AutofillService extends android.app.Service {
     ctor public AutofillService();
-    method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
+    method @Deprecated @FlaggedApi("android.service.autofill.autofill_session_destroyed") @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
     method public abstract void onFillRequest(@NonNull android.service.autofill.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
     method public abstract void onSaveRequest(@NonNull android.service.autofill.SaveRequest, @NonNull android.service.autofill.SaveCallback);
     method public void onSavedDatasetsInfoRequest(@NonNull android.service.autofill.SavedDatasetsInfoCallback);
+    method @FlaggedApi("android.service.autofill.autofill_session_destroyed") public void onSessionDestroyed(@Nullable android.service.autofill.FillEventHistory);
     field public static final String EXTRA_FILL_RESPONSE = "android.service.autofill.extra.FILL_RESPONSE";
     field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
     field public static final String SERVICE_META_DATA = "android.autofill";
diff --git a/core/java/android/app/AppOpsManager.aidl b/core/java/android/app/AppOpsManager.aidl
index b4dee2e..56ed290 100644
--- a/core/java/android/app/AppOpsManager.aidl
+++ b/core/java/android/app/AppOpsManager.aidl
@@ -19,6 +19,7 @@
 parcelable AppOpsManager.PackageOps;
 parcelable AppOpsManager.NoteOpEventProxyInfo;
 parcelable AppOpsManager.NoteOpEvent;
+parcelable AppOpsManager.NotedOp;
 parcelable AppOpsManager.OpFeatureEntry;
 parcelable AppOpsManager.OpEntry;
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1913812..c789e28 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -262,6 +262,24 @@
 
     private static final Object sLock = new Object();
 
+    // A map that records noted times for each op.
+    private final ArrayMap<NotedOp, Integer> mPendingNotedOps = new ArrayMap<>();
+    private final HandlerThread mHandlerThread;
+    private final Handler mHandler;
+    private static final int NOTE_OP_BATCHING_DELAY_MILLIS = 1000;
+
+    private boolean isNoteOpBatchingSupported() {
+        // If noteOp is called from system server no IPC is made, hence we don't need batching.
+        if (Process.myUid() == Process.SYSTEM_UID) {
+            return false;
+        }
+        return Flags.noteOpBatchingEnabled();
+    }
+
+    private final Object mBatchedNoteOpLock = new Object();
+    @GuardedBy("mBatchedNoteOpLock")
+    private boolean mIsBatchedNoteOpCallScheduled = false;
+
     /** Current {@link OnOpNotedCallback}. Change via {@link #setOnOpNotedCallback} */
     @GuardedBy("sLock")
     private static @Nullable OnOpNotedCallback sOnOpNotedCallback;
@@ -7466,6 +7484,135 @@
     }
 
     /**
+     * A NotedOp is an app op grouped in noteOp API and sent to the system server in a batch
+     *
+     * @hide
+     */
+    public static final class NotedOp implements Parcelable {
+        private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
+        private final @IntRange(from = 0) int mUid;
+        private final @Nullable String mPackageName;
+        private final @Nullable String mAttributionTag;
+        private final int mVirtualDeviceId;
+        private final @Nullable String mMessage;
+        private final boolean mShouldCollectAsyncNotedOp;
+        private final boolean mShouldCollectMessage;
+
+        public NotedOp(int op, int uid, @Nullable String packageName,
+                @Nullable String attributionTag, int virtualDeviceId, @Nullable String message,
+                boolean shouldCollectAsyncNotedOp, boolean shouldCollectMessage) {
+            mOp = op;
+            mUid = uid;
+            mPackageName = packageName;
+            mAttributionTag = attributionTag;
+            mVirtualDeviceId = virtualDeviceId;
+            mMessage = message;
+            mShouldCollectAsyncNotedOp = shouldCollectAsyncNotedOp;
+            mShouldCollectMessage = shouldCollectMessage;
+        }
+
+        NotedOp(Parcel source) {
+            mOp = source.readInt();
+            mUid = source.readInt();
+            mPackageName = source.readString();
+            mAttributionTag = source.readString();
+            mVirtualDeviceId = source.readInt();
+            mMessage = source.readString();
+            mShouldCollectAsyncNotedOp = source.readBoolean();
+            mShouldCollectMessage = source.readBoolean();
+        }
+
+        public int getOp() {
+            return mOp;
+        }
+
+        public int getUid() {
+            return mUid;
+        }
+
+        public @Nullable String getPackageName() {
+            return mPackageName;
+        }
+
+        public @Nullable String getAttributionTag() {
+            return mAttributionTag;
+        }
+
+        public int getVirtualDeviceId() {
+            return mVirtualDeviceId;
+        }
+
+        public @Nullable String getMessage() {
+            return mMessage;
+        }
+
+        public boolean getShouldCollectAsyncNotedOp() {
+            return mShouldCollectAsyncNotedOp;
+        }
+
+        public boolean getShouldCollectMessage() {
+            return mShouldCollectMessage;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mOp);
+            dest.writeInt(mUid);
+            dest.writeString(mPackageName);
+            dest.writeString(mAttributionTag);
+            dest.writeInt(mVirtualDeviceId);
+            dest.writeString(mMessage);
+            dest.writeBoolean(mShouldCollectAsyncNotedOp);
+            dest.writeBoolean(mShouldCollectMessage);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            NotedOp that = (NotedOp) o;
+            return mOp == that.mOp && mUid == that.mUid && Objects.equals(mPackageName,
+                    that.mPackageName) && Objects.equals(mAttributionTag, that.mAttributionTag)
+                    && mVirtualDeviceId == that.mVirtualDeviceId && Objects.equals(mMessage,
+                    that.mMessage) && Objects.equals(mShouldCollectAsyncNotedOp,
+                    that.mShouldCollectAsyncNotedOp) && Objects.equals(mShouldCollectMessage,
+                    that.mShouldCollectMessage);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mOp, mUid, mPackageName, mAttributionTag, mVirtualDeviceId,
+                    mMessage, mShouldCollectAsyncNotedOp, mShouldCollectMessage);
+        }
+
+        @Override
+        public String toString() {
+            return "NotedOp{" + "mOp=" + mOp + ", mUid=" + mUid + ", mPackageName=" + mPackageName
+                    + ", mAttributionTag=" + mAttributionTag + ", mVirtualDeviceId="
+                    + mVirtualDeviceId + ", mMessage=" + mMessage + ", mShouldCollectAsyncNotedOp="
+                    + mShouldCollectAsyncNotedOp + ", mShouldCollectMessage="
+                    + mShouldCollectMessage + "}";
+        }
+
+
+        public static final @NonNull Creator<NotedOp> CREATOR =
+                new Creator<>() {
+                    @Override public NotedOp createFromParcel(Parcel source) {
+                        return new NotedOp(source);
+                    }
+
+                    @Override public NotedOp[] newArray(int size) {
+                        return new NotedOp[size];
+                    }
+                };
+    }
+
+    /**
      * Computes the sum of the counts for the given flags in between the begin and
      * end UID states.
      *
@@ -7979,6 +8126,9 @@
     AppOpsManager(Context context, IAppOpsService service) {
         mContext = context;
         mService = service;
+        mHandlerThread = new HandlerThread("AppOpsManager");
+        mHandlerThread.start();
+        mHandler = mHandlerThread.getThreadHandler();
 
         if (mContext != null) {
             final PackageManager pm = mContext.getPackageManager();
@@ -9315,15 +9465,74 @@
                 }
             }
 
-            SyncNotedAppOp syncOp;
-            if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
-                syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
-                        collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
-            } else {
-                syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
-                    virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
-                    shouldCollectMessage);
+            SyncNotedAppOp syncOp = null;
+            boolean skipBinderCall = false;
+            if (isNoteOpBatchingSupported()) {
+                int mode = sAppOpModeCache.query(
+                        new AppOpModeQuery(op, uid, packageName, virtualDeviceId, attributionTag,
+                                "noteOpNoThrow"));
+                // For FOREGROUND mode, we still need to make a binder call to the system service
+                // to translate it to ALLOWED or IGNORED. So no batching is needed.
+                if (mode != MODE_FOREGROUND) {
+                    synchronized (mBatchedNoteOpLock) {
+                        NotedOp notedOp = new NotedOp(op, uid, packageName, attributionTag,
+                                virtualDeviceId, message, collectionMode == COLLECT_ASYNC,
+                                shouldCollectMessage);
+
+                        // Batch same noteOp calls and send them with their counters to the system
+                        // service asynchronously. The time window for batching is specified in
+                        // NOTE_OP_BATCHING_DELAY_MILLIS. Always allow the first noteOp call to go
+                        // through binder API. Accumulate subsequent same noteOp calls during the
+                        // time window in mPendingNotedOps.
+                        if (!mPendingNotedOps.containsKey(notedOp)) {
+                            mPendingNotedOps.put(notedOp, 0);
+                        } else {
+                            skipBinderCall = true;
+                            mPendingNotedOps.merge(notedOp, 1, Integer::sum);
+                        }
+
+                        if (!mIsBatchedNoteOpCallScheduled) {
+                            mHandler.postDelayed(() -> {
+                                ArrayMap<NotedOp, Integer> pendingNotedOpsCopy;
+                                synchronized(mBatchedNoteOpLock) {
+                                    mIsBatchedNoteOpCallScheduled = false;
+                                    pendingNotedOpsCopy =
+                                            new ArrayMap<NotedOp, Integer>(mPendingNotedOps);
+                                    mPendingNotedOps.clear();
+                                }
+                                for (int i = pendingNotedOpsCopy.size() - 1; i >= 0; i--) {
+                                    if (pendingNotedOpsCopy.valueAt(i) == 0) {
+                                        pendingNotedOpsCopy.removeAt(i);
+                                    }
+                                }
+                                if (!pendingNotedOpsCopy.isEmpty()) {
+                                    try {
+                                        mService.noteOperationsInBatch(pendingNotedOpsCopy);
+                                    } catch (RemoteException e) {
+                                        throw e.rethrowFromSystemServer();
+                                    }
+                                }
+                            }, NOTE_OP_BATCHING_DELAY_MILLIS);
+
+                            mIsBatchedNoteOpCallScheduled = true;
+                        }
+                    }
+
+                    syncOp = new SyncNotedAppOp(mode, op, attributionTag, packageName);
+                }
             }
+
+            if (!skipBinderCall) {
+                if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
+                    syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
+                            collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+                } else {
+                    syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
+                            virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
+                            shouldCollectMessage);
+                }
+            }
+
             if (syncOp.getOpMode() == MODE_ALLOWED) {
                 if (collectionMode == COLLECT_SELF) {
                     collectNotedOpForSelf(syncOp);
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index b21defb..8b7ea0f 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -29,7 +29,7 @@
 import com.android.internal.util.function.DodecFunction;
 import com.android.internal.util.function.HexConsumer;
 import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.NonaFunction;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.UndecFunction;
 
@@ -86,9 +86,9 @@
          */
         SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
                 @Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
-                @Nullable String message, boolean shouldCollectMessage,
-                @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
-                        Boolean, SyncNotedAppOp> superImpl);
+                @Nullable String message, boolean shouldCollectMessage, int notedCount,
+                @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
+                        Boolean, Integer, SyncNotedAppOp> superImpl);
 
         /**
          * Allows overriding note proxy operation behavior.
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 22bc356..361ba73 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -398,6 +398,7 @@
 
 flag {
   name: "split_create_managed_profile_enabled"
+  is_exported: true
   namespace: "enterprise"
   description: "Split up existing create and provision managed profile API."
   bug: "375382324"
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index bf7116d..6855d95 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -50,6 +50,7 @@
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
+import dalvik.annotation.optimization.NeverInline;
 
 import libcore.util.SneakyThrow;
 
@@ -587,6 +588,17 @@
         return parcel;
     }
 
+    @NeverInline
+    private void errorUsedWhileRecycling() {
+        Log.wtf(TAG, "Parcel used while recycled. "
+                + Log.getStackTraceString(new Throwable())
+                + " Original recycle call (if DEBUG_RECYCLE): ", mStack);
+    }
+
+    private void assertNotRecycled() {
+        if (mRecycled) errorUsedWhileRecycling();
+    }
+
     /**
      * Put a Parcel object back into the pool.  You must not touch
      * the object after this call.
@@ -635,6 +647,7 @@
      * @hide
      */
     public void setReadWriteHelper(@Nullable ReadWriteHelper helper) {
+        assertNotRecycled();
         mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT;
     }
 
@@ -644,6 +657,7 @@
      * @hide
      */
     public boolean hasReadWriteHelper() {
+        assertNotRecycled();
         return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
     }
 
@@ -670,6 +684,7 @@
      * @hide
      */
     public final void markSensitive() {
+        assertNotRecycled();
         nativeMarkSensitive(mNativePtr);
     }
 
@@ -686,6 +701,7 @@
      * @hide
      */
     public final boolean isForRpc() {
+        assertNotRecycled();
         return nativeIsForRpc(mNativePtr);
     }
 
@@ -693,21 +709,25 @@
     @ParcelFlags
     @TestApi
     public int getFlags() {
+        assertNotRecycled();
         return mFlags;
     }
 
     /** @hide */
     public void setFlags(@ParcelFlags int flags) {
+        assertNotRecycled();
         mFlags = flags;
     }
 
     /** @hide */
     public void addFlags(@ParcelFlags int flags) {
+        assertNotRecycled();
         mFlags |= flags;
     }
 
     /** @hide */
     private boolean hasFlags(@ParcelFlags int flags) {
+        assertNotRecycled();
         return (mFlags & flags) == flags;
     }
 
@@ -720,6 +740,7 @@
     // We don't really need to protect it; even if 3p / non-system apps, nothing would happen.
     // This would only work when used on a reply parcel by a binder object that's allowed-blocking.
     public void setPropagateAllowBlocking() {
+        assertNotRecycled();
         addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING);
     }
 
@@ -727,6 +748,7 @@
      * Returns the total amount of data contained in the parcel.
      */
     public int dataSize() {
+        assertNotRecycled();
         return nativeDataSize(mNativePtr);
     }
 
@@ -735,6 +757,7 @@
      * parcel.  That is, {@link #dataSize}-{@link #dataPosition}.
      */
     public final int dataAvail() {
+        assertNotRecycled();
         return nativeDataAvail(mNativePtr);
     }
 
@@ -743,6 +766,7 @@
      * more than {@link #dataSize}.
      */
     public final int dataPosition() {
+        assertNotRecycled();
         return nativeDataPosition(mNativePtr);
     }
 
@@ -753,6 +777,7 @@
      * data buffer.
      */
     public final int dataCapacity() {
+        assertNotRecycled();
         return nativeDataCapacity(mNativePtr);
     }
 
@@ -764,6 +789,7 @@
      * @param size The new number of bytes in the Parcel.
      */
     public final void setDataSize(int size) {
+        assertNotRecycled();
         nativeSetDataSize(mNativePtr, size);
     }
 
@@ -773,6 +799,7 @@
      * {@link #dataSize}.
      */
     public final void setDataPosition(int pos) {
+        assertNotRecycled();
         nativeSetDataPosition(mNativePtr, pos);
     }
 
@@ -784,11 +811,13 @@
      * with this method.
      */
     public final void setDataCapacity(int size) {
+        assertNotRecycled();
         nativeSetDataCapacity(mNativePtr, size);
     }
 
     /** @hide */
     public final boolean pushAllowFds(boolean allowFds) {
+        assertNotRecycled();
         return nativePushAllowFds(mNativePtr, allowFds);
     }
 
@@ -809,6 +838,7 @@
      * in different versions of the platform.
      */
     public final byte[] marshall() {
+        assertNotRecycled();
         return nativeMarshall(mNativePtr);
     }
 
@@ -816,15 +846,18 @@
      * Fills the raw bytes of this Parcel with the supplied data.
      */
     public final void unmarshall(@NonNull byte[] data, int offset, int length) {
+        assertNotRecycled();
         nativeUnmarshall(mNativePtr, data, offset, length);
     }
 
     public final void appendFrom(Parcel parcel, int offset, int length) {
+        assertNotRecycled();
         nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
     }
 
     /** @hide */
     public int compareData(Parcel other) {
+        assertNotRecycled();
         return nativeCompareData(mNativePtr, other.mNativePtr);
     }
 
@@ -835,6 +868,7 @@
 
     /** @hide */
     public final void setClassCookie(Class clz, Object cookie) {
+        assertNotRecycled();
         if (mClassCookies == null) {
             mClassCookies = new ArrayMap<>();
         }
@@ -844,11 +878,13 @@
     /** @hide */
     @Nullable
     public final Object getClassCookie(Class clz) {
+        assertNotRecycled();
         return mClassCookies != null ? mClassCookies.get(clz) : null;
     }
 
     /** @hide */
     public void removeClassCookie(Class clz, Object expectedCookie) {
+        assertNotRecycled();
         if (mClassCookies != null) {
             Object removedCookie = mClassCookies.remove(clz);
             if (removedCookie != expectedCookie) {
@@ -866,21 +902,25 @@
      * @hide
      */
     public boolean hasClassCookie(Class clz) {
+        assertNotRecycled();
         return mClassCookies != null && mClassCookies.containsKey(clz);
     }
 
     /** @hide */
     public final void adoptClassCookies(Parcel from) {
+        assertNotRecycled();
         mClassCookies = from.mClassCookies;
     }
 
     /** @hide */
     public Map<Class, Object> copyClassCookies() {
+        assertNotRecycled();
         return new ArrayMap<>(mClassCookies);
     }
 
     /** @hide */
     public void putClassCookies(Map<Class, Object> cookies) {
+        assertNotRecycled();
         if (cookies == null) {
             return;
         }
@@ -894,6 +934,7 @@
      * Report whether the parcel contains any marshalled file descriptors.
      */
     public boolean hasFileDescriptors() {
+        assertNotRecycled();
         return nativeHasFileDescriptors(mNativePtr);
     }
 
@@ -909,6 +950,7 @@
      * @throws IllegalArgumentException if the parameters are out of the permitted ranges.
      */
     public boolean hasFileDescriptors(int offset, int length) {
+        assertNotRecycled();
         return nativeHasFileDescriptorsInRange(mNativePtr, offset, length);
     }
 
@@ -993,6 +1035,7 @@
      * @hide
      */
     public boolean hasBinders() {
+        assertNotRecycled();
         return nativeHasBinders(mNativePtr);
     }
 
@@ -1010,6 +1053,7 @@
      * @hide
      */
     public boolean hasBinders(int offset, int length) {
+        assertNotRecycled();
         return nativeHasBindersInRange(mNativePtr, offset, length);
     }
 
@@ -1020,6 +1064,7 @@
      * at the beginning of transactions as a header.
      */
     public final void writeInterfaceToken(@NonNull String interfaceName) {
+        assertNotRecycled();
         nativeWriteInterfaceToken(mNativePtr, interfaceName);
     }
 
@@ -1030,6 +1075,7 @@
      * should propagate to the caller.
      */
     public final void enforceInterface(@NonNull String interfaceName) {
+        assertNotRecycled();
         nativeEnforceInterface(mNativePtr, interfaceName);
     }
 
@@ -1040,6 +1086,7 @@
      * When used over binder, this exception should propagate to the caller.
      */
     public void enforceNoDataAvail() {
+        assertNotRecycled();
         final int n = dataAvail();
         if (n > 0) {
             throw new BadParcelableException("Parcel data not fully consumed, unread size: " + n);
@@ -1056,6 +1103,7 @@
      * @hide
      */
     public boolean replaceCallingWorkSourceUid(int workSourceUid) {
+        assertNotRecycled();
         return nativeReplaceCallingWorkSourceUid(mNativePtr, workSourceUid);
     }
 
@@ -1072,6 +1120,7 @@
      * @hide
      */
     public int readCallingWorkSourceUid() {
+        assertNotRecycled();
         return nativeReadCallingWorkSourceUid(mNativePtr);
     }
 
@@ -1081,6 +1130,7 @@
      * @param b Bytes to place into the parcel.
      */
     public final void writeByteArray(@Nullable byte[] b) {
+        assertNotRecycled();
         writeByteArray(b, 0, (b != null) ? b.length : 0);
     }
 
@@ -1092,6 +1142,7 @@
      * @param len Number of bytes to write.
      */
     public final void writeByteArray(@Nullable byte[] b, int offset, int len) {
+        assertNotRecycled();
         if (b == null) {
             writeInt(-1);
             return;
@@ -1113,6 +1164,7 @@
      * @see #readBlob()
      */
     public final void writeBlob(@Nullable byte[] b) {
+        assertNotRecycled();
         writeBlob(b, 0, (b != null) ? b.length : 0);
     }
 
@@ -1131,6 +1183,7 @@
      * @see #readBlob()
      */
     public final void writeBlob(@Nullable byte[] b, int offset, int len) {
+        assertNotRecycled();
         if (b == null) {
             writeInt(-1);
             return;
@@ -1149,6 +1202,7 @@
      * growing dataCapacity() if needed.
      */
     public final void writeInt(int val) {
+        assertNotRecycled();
         int err = nativeWriteInt(mNativePtr, val);
         if (err != OK) {
             nativeSignalExceptionForError(err);
@@ -1160,6 +1214,7 @@
      * growing dataCapacity() if needed.
      */
     public final void writeLong(long val) {
+        assertNotRecycled();
         int err = nativeWriteLong(mNativePtr, val);
         if (err != OK) {
             nativeSignalExceptionForError(err);
@@ -1171,6 +1226,7 @@
      * dataPosition(), growing dataCapacity() if needed.
      */
     public final void writeFloat(float val) {
+        assertNotRecycled();
         int err = nativeWriteFloat(mNativePtr, val);
         if (err != OK) {
             nativeSignalExceptionForError(err);
@@ -1182,6 +1238,7 @@
      * current dataPosition(), growing dataCapacity() if needed.
      */
     public final void writeDouble(double val) {
+        assertNotRecycled();
         int err = nativeWriteDouble(mNativePtr, val);
         if (err != OK) {
             nativeSignalExceptionForError(err);
@@ -1193,16 +1250,19 @@
      * growing dataCapacity() if needed.
      */
     public final void writeString(@Nullable String val) {
+        assertNotRecycled();
         writeString16(val);
     }
 
     /** {@hide} */
     public final void writeString8(@Nullable String val) {
+        assertNotRecycled();
         mReadWriteHelper.writeString8(this, val);
     }
 
     /** {@hide} */
     public final void writeString16(@Nullable String val) {
+        assertNotRecycled();
         mReadWriteHelper.writeString16(this, val);
     }
 
@@ -1214,16 +1274,19 @@
      * @hide
      */
     public void writeStringNoHelper(@Nullable String val) {
+        assertNotRecycled();
         writeString16NoHelper(val);
     }
 
     /** {@hide} */
     public void writeString8NoHelper(@Nullable String val) {
+        assertNotRecycled();
         nativeWriteString8(mNativePtr, val);
     }
 
     /** {@hide} */
     public void writeString16NoHelper(@Nullable String val) {
+        assertNotRecycled();
         nativeWriteString16(mNativePtr, val);
     }
 
@@ -1235,6 +1298,7 @@
      * for true or false, respectively, but may change in the future.
      */
     public final void writeBoolean(boolean val) {
+        assertNotRecycled();
         writeInt(val ? 1 : 0);
     }
 
@@ -1246,6 +1310,7 @@
     @UnsupportedAppUsage
     @RavenwoodThrow(blockedBy = android.text.Spanned.class)
     public final void writeCharSequence(@Nullable CharSequence val) {
+        assertNotRecycled();
         TextUtils.writeToParcel(val, this, 0);
     }
 
@@ -1254,6 +1319,7 @@
      * growing dataCapacity() if needed.
      */
     public final void writeStrongBinder(IBinder val) {
+        assertNotRecycled();
         nativeWriteStrongBinder(mNativePtr, val);
     }
 
@@ -1262,6 +1328,7 @@
      * growing dataCapacity() if needed.
      */
     public final void writeStrongInterface(IInterface val) {
+        assertNotRecycled();
         writeStrongBinder(val == null ? null : val.asBinder());
     }
 
@@ -1276,6 +1343,7 @@
      * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
      */
     public final void writeFileDescriptor(@NonNull FileDescriptor val) {
+        assertNotRecycled();
         nativeWriteFileDescriptor(mNativePtr, val);
     }
 
@@ -1284,6 +1352,7 @@
      * This will be the new name for writeFileDescriptor, for consistency.
      **/
     public final void writeRawFileDescriptor(@NonNull FileDescriptor val) {
+        assertNotRecycled();
         nativeWriteFileDescriptor(mNativePtr, val);
     }
 
@@ -1294,6 +1363,7 @@
      * @param value The array of objects to be written.
      */
     public final void writeRawFileDescriptorArray(@Nullable FileDescriptor[] value) {
+        assertNotRecycled();
         if (value != null) {
             int N = value.length;
             writeInt(N);
@@ -1313,6 +1383,7 @@
      * the future.
      */
     public final void writeByte(byte val) {
+        assertNotRecycled();
         writeInt(val);
     }
 
@@ -1328,6 +1399,7 @@
      * allows you to avoid mysterious type errors at the point of marshalling.
      */
     public final void writeMap(@Nullable Map val) {
+        assertNotRecycled();
         writeMapInternal((Map<String, Object>) val);
     }
 
@@ -1336,6 +1408,7 @@
      * growing dataCapacity() if needed.  The Map keys must be String objects.
      */
     /* package */ void writeMapInternal(@Nullable Map<String,Object> val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1361,6 +1434,7 @@
      * growing dataCapacity() if needed.  The Map keys must be String objects.
      */
     /* package */ void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1390,6 +1464,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void writeArrayMap(@Nullable ArrayMap<String, Object> val) {
+        assertNotRecycled();
         writeArrayMapInternal(val);
     }
 
@@ -1408,6 +1483,7 @@
      */
     public <T extends Parcelable> void writeTypedArrayMap(@Nullable ArrayMap<String, T> val,
             int parcelableFlags) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1429,6 +1505,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void writeArraySet(@Nullable ArraySet<? extends Object> val) {
+        assertNotRecycled();
         final int size = (val != null) ? val.size() : -1;
         writeInt(size);
         for (int i = 0; i < size; i++) {
@@ -1441,6 +1518,7 @@
      * growing dataCapacity() if needed.
      */
     public final void writeBundle(@Nullable Bundle val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1454,6 +1532,7 @@
      * growing dataCapacity() if needed.
      */
     public final void writePersistableBundle(@Nullable PersistableBundle val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1467,6 +1546,7 @@
      * growing dataCapacity() if needed.
      */
     public final void writeSize(@NonNull Size val) {
+        assertNotRecycled();
         writeInt(val.getWidth());
         writeInt(val.getHeight());
     }
@@ -1476,6 +1556,7 @@
      * growing dataCapacity() if needed.
      */
     public final void writeSizeF(@NonNull SizeF val) {
+        assertNotRecycled();
         writeFloat(val.getWidth());
         writeFloat(val.getHeight());
     }
@@ -1486,6 +1567,7 @@
      * {@link #writeValue} and must follow the specification there.
      */
     public final void writeList(@Nullable List val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1505,6 +1587,7 @@
      * {@link #writeValue} and must follow the specification there.
      */
     public final void writeArray(@Nullable Object[] val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1525,6 +1608,7 @@
      * specification there.
      */
     public final <T> void writeSparseArray(@Nullable SparseArray<T> val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1540,6 +1624,7 @@
     }
 
     public final void writeSparseBooleanArray(@Nullable SparseBooleanArray val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1558,6 +1643,7 @@
      * @hide
      */
     public final void writeSparseIntArray(@Nullable SparseIntArray val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -1573,6 +1659,7 @@
     }
 
     public final void writeBooleanArray(@Nullable boolean[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -1607,6 +1694,7 @@
     }
 
     private void ensureWithinMemoryLimit(int typeSize, @NonNull int... dimensions) {
+        assertNotRecycled();
         // For Multidimensional arrays, Calculate total object
         // which will be allocated.
         int totalObjects = 1;
@@ -1624,6 +1712,7 @@
     }
 
     private void ensureWithinMemoryLimit(int typeSize, int length) {
+        assertNotRecycled();
         int estimatedAllocationSize = 0;
         try {
             estimatedAllocationSize = Math.multiplyExact(typeSize, length);
@@ -1647,6 +1736,7 @@
 
     @Nullable
     public final boolean[] createBooleanArray() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_BOOLEAN, N);
         // >>2 as a fast divide-by-4 works in the create*Array() functions
@@ -1664,6 +1754,7 @@
     }
 
     public final void readBooleanArray(@NonNull boolean[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -1676,6 +1767,7 @@
 
     /** @hide */
     public void writeShortArray(@Nullable short[] val) {
+        assertNotRecycled();
         if (val != null) {
             int n = val.length;
             writeInt(n);
@@ -1690,6 +1782,7 @@
     /** @hide */
     @Nullable
     public short[] createShortArray() {
+        assertNotRecycled();
         int n = readInt();
         ensureWithinMemoryLimit(SIZE_SHORT, n);
         if (n >= 0 && n <= (dataAvail() >> 2)) {
@@ -1705,6 +1798,7 @@
 
     /** @hide */
     public void readShortArray(@NonNull short[] val) {
+        assertNotRecycled();
         int n = readInt();
         if (n == val.length) {
             for (int i = 0; i < n; i++) {
@@ -1716,6 +1810,7 @@
     }
 
     public final void writeCharArray(@Nullable char[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -1729,6 +1824,7 @@
 
     @Nullable
     public final char[] createCharArray() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_CHAR, N);
         if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1743,6 +1839,7 @@
     }
 
     public final void readCharArray(@NonNull char[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -1754,6 +1851,7 @@
     }
 
     public final void writeIntArray(@Nullable int[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -1767,6 +1865,7 @@
 
     @Nullable
     public final int[] createIntArray() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_INT, N);
         if (N >= 0 && N <= (dataAvail() >> 2)) {
@@ -1781,6 +1880,7 @@
     }
 
     public final void readIntArray(@NonNull int[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -1792,6 +1892,7 @@
     }
 
     public final void writeLongArray(@Nullable long[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -1805,6 +1906,7 @@
 
     @Nullable
     public final long[] createLongArray() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_LONG, N);
         // >>3 because stored longs are 64 bits
@@ -1820,6 +1922,7 @@
     }
 
     public final void readLongArray(@NonNull long[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -1831,6 +1934,7 @@
     }
 
     public final void writeFloatArray(@Nullable float[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -1844,6 +1948,7 @@
 
     @Nullable
     public final float[] createFloatArray() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_FLOAT, N);
         // >>2 because stored floats are 4 bytes
@@ -1859,6 +1964,7 @@
     }
 
     public final void readFloatArray(@NonNull float[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -1870,6 +1976,7 @@
     }
 
     public final void writeDoubleArray(@Nullable double[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -1883,6 +1990,7 @@
 
     @Nullable
     public final double[] createDoubleArray() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_DOUBLE, N);
         // >>3 because stored doubles are 8 bytes
@@ -1898,6 +2006,7 @@
     }
 
     public final void readDoubleArray(@NonNull double[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -1909,20 +2018,24 @@
     }
 
     public final void writeStringArray(@Nullable String[] val) {
+        assertNotRecycled();
         writeString16Array(val);
     }
 
     @Nullable
     public final String[] createStringArray() {
+        assertNotRecycled();
         return createString16Array();
     }
 
     public final void readStringArray(@NonNull String[] val) {
+        assertNotRecycled();
         readString16Array(val);
     }
 
     /** {@hide} */
     public final void writeString8Array(@Nullable String[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -1937,6 +2050,7 @@
     /** {@hide} */
     @Nullable
     public final String[] createString8Array() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         if (N >= 0) {
@@ -1952,6 +2066,7 @@
 
     /** {@hide} */
     public final void readString8Array(@NonNull String[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -1964,6 +2079,7 @@
 
     /** {@hide} */
     public final void writeString16Array(@Nullable String[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -1978,6 +2094,7 @@
     /** {@hide} */
     @Nullable
     public final String[] createString16Array() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         if (N >= 0) {
@@ -1993,6 +2110,7 @@
 
     /** {@hide} */
     public final void readString16Array(@NonNull String[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -2004,6 +2122,7 @@
     }
 
     public final void writeBinderArray(@Nullable IBinder[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -2028,6 +2147,7 @@
      */
     public final <T extends IInterface> void writeInterfaceArray(
             @SuppressLint("ArrayReturn") @Nullable T[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -2043,6 +2163,7 @@
      * @hide
      */
     public final void writeCharSequenceArray(@Nullable CharSequence[] val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -2058,6 +2179,7 @@
      * @hide
      */
     public final void writeCharSequenceList(@Nullable ArrayList<CharSequence> val) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.size();
             writeInt(N);
@@ -2071,6 +2193,7 @@
 
     @Nullable
     public final IBinder[] createBinderArray() {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         if (N >= 0) {
@@ -2085,6 +2208,7 @@
     }
 
     public final void readBinderArray(@NonNull IBinder[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -2106,6 +2230,7 @@
     @Nullable
     public final <T extends IInterface> T[] createInterfaceArray(
             @NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) {
+        assertNotRecycled();
         int N = readInt();
         ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         if (N >= 0) {
@@ -2130,6 +2255,7 @@
     public final <T extends IInterface> void readInterfaceArray(
             @SuppressLint("ArrayReturn") @NonNull T[] val,
             @NonNull Function<IBinder, T> asInterface) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -2155,6 +2281,7 @@
      * @see Parcelable
      */
     public final <T extends Parcelable> void writeTypedList(@Nullable List<T> val) {
+        assertNotRecycled();
         writeTypedList(val, 0);
     }
 
@@ -2174,6 +2301,7 @@
      */
     public final <T extends Parcelable> void writeTypedSparseArray(@Nullable SparseArray<T> val,
             int parcelableFlags) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -2203,6 +2331,7 @@
      * @see Parcelable
      */
     public <T extends Parcelable> void writeTypedList(@Nullable List<T> val, int parcelableFlags) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -2228,6 +2357,7 @@
      * @see #readStringList
      */
     public final void writeStringList(@Nullable List<String> val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -2253,6 +2383,7 @@
      * @see #readBinderList
      */
     public final void writeBinderList(@Nullable List<IBinder> val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -2275,6 +2406,7 @@
      * @see #readInterfaceList
      */
     public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -2296,6 +2428,7 @@
      * @see #readParcelableList(List, ClassLoader)
      */
     public final <T extends Parcelable> void writeParcelableList(@Nullable List<T> val, int flags) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -2330,6 +2463,7 @@
      */
     public final <T extends Parcelable> void writeTypedArray(@Nullable T[] val,
             int parcelableFlags) {
+        assertNotRecycled();
         if (val != null) {
             int N = val.length;
             writeInt(N);
@@ -2352,6 +2486,7 @@
      */
     public final <T extends Parcelable> void writeTypedObject(@Nullable T val,
             int parcelableFlags) {
+        assertNotRecycled();
         if (val != null) {
             writeInt(1);
             val.writeToParcel(this, parcelableFlags);
@@ -2389,6 +2524,7 @@
      */
     public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
             @NonNull int... dimensions) {
+        assertNotRecycled();
         if (val == null) {
             writeInt(-1);
             return;
@@ -2500,6 +2636,7 @@
      * should be used).</p>
      */
     public final void writeValue(@Nullable Object v) {
+        assertNotRecycled();
         if (v instanceof LazyValue) {
             LazyValue value = (LazyValue) v;
             value.writeToParcel(this);
@@ -2617,6 +2754,7 @@
      * @hide
      */
     public void writeValue(int type, @Nullable Object v) {
+        assertNotRecycled();
         switch (type) {
             case VAL_NULL:
                 break;
@@ -2730,6 +2868,7 @@
      * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
      */
     public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
+        assertNotRecycled();
         if (p == null) {
             writeString(null);
             return;
@@ -2745,6 +2884,7 @@
      * @see #readParcelableCreator
      */
     public final void writeParcelableCreator(@NonNull Parcelable p) {
+        assertNotRecycled();
         String name = p.getClass().getName();
         writeString(name);
     }
@@ -2783,6 +2923,7 @@
      */
     @TestApi
     public boolean allowSquashing() {
+        assertNotRecycled();
         boolean previous = mAllowSquashing;
         mAllowSquashing = true;
         return previous;
@@ -2794,6 +2935,7 @@
      */
     @TestApi
     public void restoreAllowSquashing(boolean previous) {
+        assertNotRecycled();
         mAllowSquashing = previous;
         if (!mAllowSquashing) {
             mWrittenSquashableParcelables = null;
@@ -2850,6 +2992,7 @@
      * @hide
      */
     public boolean maybeWriteSquashed(@NonNull Parcelable p) {
+        assertNotRecycled();
         if (!mAllowSquashing) {
             // Don't squash, and don't put it in the map either.
             writeInt(0);
@@ -2900,6 +3043,7 @@
     @SuppressWarnings("unchecked")
     @Nullable
     public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) {
+        assertNotRecycled();
         final int offset = readInt();
         final int pos = dataPosition();
 
@@ -2933,6 +3077,7 @@
      * using the other approaches to writing data in to a Parcel.
      */
     public final void writeSerializable(@Nullable Serializable s) {
+        assertNotRecycled();
         if (s == null) {
             writeString(null);
             return;
@@ -2985,6 +3130,7 @@
      */
     @RavenwoodReplace(blockedBy = AppOpsManager.class)
     public final void writeException(@NonNull Exception e) {
+        assertNotRecycled();
         AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
 
         int code = getExceptionCode(e);
@@ -3065,6 +3211,7 @@
 
     /** @hide */
     public void writeStackTrace(@NonNull Throwable e) {
+        assertNotRecycled();
         final int sizePosition = dataPosition();
         writeInt(0); // Header size will be filled in later
         StackTraceElement[] stackTrace = e.getStackTrace();
@@ -3090,6 +3237,7 @@
      */
     @RavenwoodReplace(blockedBy = AppOpsManager.class)
     public final void writeNoException() {
+        assertNotRecycled();
         AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
 
         // Despite the name of this function ("write no exception"),
@@ -3133,6 +3281,7 @@
      * @see #writeNoException
      */
     public final void readException() {
+        assertNotRecycled();
         int code = readExceptionCode();
         if (code != 0) {
             String msg = readString();
@@ -3156,6 +3305,7 @@
     @UnsupportedAppUsage
     @TestApi
     public final int readExceptionCode() {
+        assertNotRecycled();
         int code = readInt();
         if (code == EX_HAS_NOTED_APPOPS_REPLY_HEADER) {
             AppOpsManager.readAndLogNotedAppops(this);
@@ -3189,6 +3339,7 @@
      * @param msg The exception message.
      */
     public final void readException(int code, String msg) {
+        assertNotRecycled();
         String remoteStackTrace = null;
         final int remoteStackPayloadSize = readInt();
         if (remoteStackPayloadSize > 0) {
@@ -3219,6 +3370,7 @@
 
     /** @hide */
     public Exception createExceptionOrNull(int code, String msg) {
+        assertNotRecycled();
         switch (code) {
             case EX_PARCELABLE:
                 if (readInt() > 0) {
@@ -3251,6 +3403,7 @@
      * Read an integer value from the parcel at the current dataPosition().
      */
     public final int readInt() {
+        assertNotRecycled();
         return nativeReadInt(mNativePtr);
     }
 
@@ -3258,6 +3411,7 @@
      * Read a long integer value from the parcel at the current dataPosition().
      */
     public final long readLong() {
+        assertNotRecycled();
         return nativeReadLong(mNativePtr);
     }
 
@@ -3266,6 +3420,7 @@
      * dataPosition().
      */
     public final float readFloat() {
+        assertNotRecycled();
         return nativeReadFloat(mNativePtr);
     }
 
@@ -3274,6 +3429,7 @@
      * current dataPosition().
      */
     public final double readDouble() {
+        assertNotRecycled();
         return nativeReadDouble(mNativePtr);
     }
 
@@ -3282,16 +3438,19 @@
      */
     @Nullable
     public final String readString() {
+        assertNotRecycled();
         return readString16();
     }
 
     /** {@hide} */
     public final @Nullable String readString8() {
+        assertNotRecycled();
         return mReadWriteHelper.readString8(this);
     }
 
     /** {@hide} */
     public final @Nullable String readString16() {
+        assertNotRecycled();
         return mReadWriteHelper.readString16(this);
     }
 
@@ -3303,16 +3462,19 @@
      * @hide
      */
     public @Nullable String readStringNoHelper() {
+        assertNotRecycled();
         return readString16NoHelper();
     }
 
     /** {@hide} */
     public @Nullable String readString8NoHelper() {
+        assertNotRecycled();
         return nativeReadString8(mNativePtr);
     }
 
     /** {@hide} */
     public @Nullable String readString16NoHelper() {
+        assertNotRecycled();
         return nativeReadString16(mNativePtr);
     }
 
@@ -3320,6 +3482,7 @@
      * Read a boolean value from the parcel at the current dataPosition().
      */
     public final boolean readBoolean() {
+        assertNotRecycled();
         return readInt() != 0;
     }
 
@@ -3330,6 +3493,7 @@
     @UnsupportedAppUsage
     @Nullable
     public final CharSequence readCharSequence() {
+        assertNotRecycled();
         return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(this);
     }
 
@@ -3337,6 +3501,7 @@
      * Read an object from the parcel at the current dataPosition().
      */
     public final IBinder readStrongBinder() {
+        assertNotRecycled();
         final IBinder result = nativeReadStrongBinder(mNativePtr);
 
         // If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking
@@ -3352,6 +3517,7 @@
      * Read a FileDescriptor from the parcel at the current dataPosition().
      */
     public final ParcelFileDescriptor readFileDescriptor() {
+        assertNotRecycled();
         FileDescriptor fd = nativeReadFileDescriptor(mNativePtr);
         return fd != null ? new ParcelFileDescriptor(fd) : null;
     }
@@ -3359,6 +3525,7 @@
     /** {@hide} */
     @UnsupportedAppUsage
     public final FileDescriptor readRawFileDescriptor() {
+        assertNotRecycled();
         return nativeReadFileDescriptor(mNativePtr);
     }
 
@@ -3369,6 +3536,7 @@
      **/
     @Nullable
     public final FileDescriptor[] createRawFileDescriptorArray() {
+        assertNotRecycled();
         int N = readInt();
         if (N < 0) {
             return null;
@@ -3388,6 +3556,7 @@
      * @return the FileDescriptor array, or null if the array is null.
      **/
     public final void readRawFileDescriptorArray(FileDescriptor[] val) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -3402,6 +3571,7 @@
      * Read a byte value from the parcel at the current dataPosition().
      */
     public final byte readByte() {
+        assertNotRecycled();
         return (byte)(readInt() & 0xff);
     }
 
@@ -3416,6 +3586,7 @@
      */
     @Deprecated
     public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
+        assertNotRecycled();
         readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
     }
 
@@ -3429,6 +3600,7 @@
     public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
             @Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
             @NonNull Class<V> clazzValue) {
+        assertNotRecycled();
         Objects.requireNonNull(clazzKey);
         Objects.requireNonNull(clazzValue);
         readMapInternal(outVal, loader, clazzKey, clazzValue);
@@ -3447,6 +3619,7 @@
      */
     @Deprecated
     public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
+        assertNotRecycled();
         int N = readInt();
         readListInternal(outVal, N, loader, /* clazz */ null);
     }
@@ -3468,6 +3641,7 @@
      */
     public <T> void readList(@NonNull List<? super T> outVal,
             @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        assertNotRecycled();
         Objects.requireNonNull(clazz);
         int n = readInt();
         readListInternal(outVal, n, loader, clazz);
@@ -3487,6 +3661,7 @@
     @Deprecated
     @Nullable
     public HashMap readHashMap(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
     }
 
@@ -3501,6 +3676,7 @@
     @Nullable
     public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
             @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+        assertNotRecycled();
         Objects.requireNonNull(clazzKey);
         Objects.requireNonNull(clazzValue);
         return readHashMapInternal(loader, clazzKey, clazzValue);
@@ -3513,6 +3689,7 @@
      */
     @Nullable
     public final Bundle readBundle() {
+        assertNotRecycled();
         return readBundle(null);
     }
 
@@ -3524,6 +3701,7 @@
      */
     @Nullable
     public final Bundle readBundle(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         int length = readInt();
         if (length < 0) {
             if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
@@ -3544,6 +3722,7 @@
      */
     @Nullable
     public final PersistableBundle readPersistableBundle() {
+        assertNotRecycled();
         return readPersistableBundle(null);
     }
 
@@ -3555,6 +3734,7 @@
      */
     @Nullable
     public final PersistableBundle readPersistableBundle(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         int length = readInt();
         if (length < 0) {
             if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
@@ -3573,6 +3753,7 @@
      */
     @NonNull
     public final Size readSize() {
+        assertNotRecycled();
         final int width = readInt();
         final int height = readInt();
         return new Size(width, height);
@@ -3583,6 +3764,7 @@
      */
     @NonNull
     public final SizeF readSizeF() {
+        assertNotRecycled();
         final float width = readFloat();
         final float height = readFloat();
         return new SizeF(width, height);
@@ -3593,6 +3775,7 @@
      */
     @Nullable
     public final byte[] createByteArray() {
+        assertNotRecycled();
         return nativeCreateByteArray(mNativePtr);
     }
 
@@ -3601,6 +3784,7 @@
      * given byte array.
      */
     public final void readByteArray(@NonNull byte[] val) {
+        assertNotRecycled();
         boolean valid = nativeReadByteArray(mNativePtr, val, (val != null) ? val.length : 0);
         if (!valid) {
             throw new RuntimeException("bad array lengths");
@@ -3613,6 +3797,7 @@
      */
     @Nullable
     public final byte[] readBlob() {
+        assertNotRecycled();
         return nativeReadBlob(mNativePtr);
     }
 
@@ -3623,6 +3808,7 @@
     @UnsupportedAppUsage
     @Nullable
     public final String[] readStringArray() {
+        assertNotRecycled();
         return createString16Array();
     }
 
@@ -3632,6 +3818,7 @@
      */
     @Nullable
     public final CharSequence[] readCharSequenceArray() {
+        assertNotRecycled();
         CharSequence[] array = null;
 
         int length = readInt();
@@ -3654,6 +3841,7 @@
      */
     @Nullable
     public final ArrayList<CharSequence> readCharSequenceList() {
+        assertNotRecycled();
         ArrayList<CharSequence> array = null;
 
         int length = readInt();
@@ -3683,6 +3871,7 @@
     @Deprecated
     @Nullable
     public ArrayList readArrayList(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         return readArrayListInternal(loader, /* clazz */ null);
     }
 
@@ -3705,6 +3894,7 @@
     @Nullable
     public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader,
             @NonNull Class<? extends T> clazz) {
+        assertNotRecycled();
         Objects.requireNonNull(clazz);
         return readArrayListInternal(loader, clazz);
     }
@@ -3724,6 +3914,7 @@
     @Deprecated
     @Nullable
     public Object[] readArray(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         return readArrayInternal(loader, /* clazz */ null);
     }
 
@@ -3745,6 +3936,7 @@
     @SuppressLint({"ArrayReturn", "NullableCollection"})
     @Nullable
     public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        assertNotRecycled();
         Objects.requireNonNull(clazz);
         return readArrayInternal(loader, clazz);
     }
@@ -3764,6 +3956,7 @@
     @Deprecated
     @Nullable
     public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         return readSparseArrayInternal(loader, /* clazz */ null);
     }
 
@@ -3785,6 +3978,7 @@
     @Nullable
     public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader,
             @NonNull Class<? extends T> clazz) {
+        assertNotRecycled();
         Objects.requireNonNull(clazz);
         return readSparseArrayInternal(loader, clazz);
     }
@@ -3796,6 +3990,7 @@
      */
     @Nullable
     public final SparseBooleanArray readSparseBooleanArray() {
+        assertNotRecycled();
         int N = readInt();
         if (N < 0) {
             return null;
@@ -3812,6 +4007,7 @@
      */
     @Nullable
     public final SparseIntArray readSparseIntArray() {
+        assertNotRecycled();
         int N = readInt();
         if (N < 0) {
             return null;
@@ -3836,6 +4032,7 @@
      */
     @Nullable
     public final <T> ArrayList<T> createTypedArrayList(@NonNull Parcelable.Creator<T> c) {
+        assertNotRecycled();
         int N = readInt();
         if (N < 0) {
             return null;
@@ -3859,6 +4056,7 @@
      * @see #writeTypedList
      */
     public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) {
+        assertNotRecycled();
         int M = list.size();
         int N = readInt();
         int i = 0;
@@ -3888,6 +4086,7 @@
      */
     public final @Nullable <T extends Parcelable> SparseArray<T> createTypedSparseArray(
             @NonNull Parcelable.Creator<T> creator) {
+        assertNotRecycled();
         final int count = readInt();
         if (count < 0) {
             return null;
@@ -3917,6 +4116,7 @@
      */
     public final @Nullable <T extends Parcelable> ArrayMap<String, T> createTypedArrayMap(
             @NonNull Parcelable.Creator<T> creator) {
+        assertNotRecycled();
         final int count = readInt();
         if (count < 0) {
             return null;
@@ -3944,6 +4144,7 @@
      */
     @Nullable
     public final ArrayList<String> createStringArrayList() {
+        assertNotRecycled();
         int N = readInt();
         if (N < 0) {
             return null;
@@ -3970,6 +4171,7 @@
      */
     @Nullable
     public final ArrayList<IBinder> createBinderArrayList() {
+        assertNotRecycled();
         int N = readInt();
         if (N < 0) {
             return null;
@@ -3997,6 +4199,7 @@
     @Nullable
     public final <T extends IInterface> ArrayList<T> createInterfaceArrayList(
             @NonNull Function<IBinder, T> asInterface) {
+        assertNotRecycled();
         int N = readInt();
         if (N < 0) {
             return null;
@@ -4017,6 +4220,7 @@
      * @see #writeStringList
      */
     public final void readStringList(@NonNull List<String> list) {
+        assertNotRecycled();
         int M = list.size();
         int N = readInt();
         int i = 0;
@@ -4038,6 +4242,7 @@
      * @see #writeBinderList
      */
     public final void readBinderList(@NonNull List<IBinder> list) {
+        assertNotRecycled();
         int M = list.size();
         int N = readInt();
         int i = 0;
@@ -4060,6 +4265,7 @@
      */
     public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list,
             @NonNull Function<IBinder, T> asInterface) {
+        assertNotRecycled();
         int M = list.size();
         int N = readInt();
         int i = 0;
@@ -4091,6 +4297,7 @@
     @NonNull
     public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
             @Nullable ClassLoader cl) {
+        assertNotRecycled();
         return readParcelableListInternal(list, cl, /*clazz*/ null);
     }
 
@@ -4112,6 +4319,7 @@
     @NonNull
     public <T> List<T> readParcelableList(@NonNull List<T> list,
             @Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) {
+        assertNotRecycled();
         Objects.requireNonNull(list);
         Objects.requireNonNull(clazz);
         return readParcelableListInternal(list, cl, clazz);
@@ -4157,6 +4365,7 @@
      */
     @Nullable
     public final <T> T[] createTypedArray(@NonNull Parcelable.Creator<T> c) {
+        assertNotRecycled();
         int N = readInt();
         if (N < 0) {
             return null;
@@ -4170,6 +4379,7 @@
     }
 
     public final <T> void readTypedArray(@NonNull T[] val, @NonNull Parcelable.Creator<T> c) {
+        assertNotRecycled();
         int N = readInt();
         if (N == val.length) {
             for (int i=0; i<N; i++) {
@@ -4186,6 +4396,7 @@
      */
     @Deprecated
     public final <T> T[] readTypedArray(Parcelable.Creator<T> c) {
+        assertNotRecycled();
         return createTypedArray(c);
     }
 
@@ -4202,6 +4413,7 @@
      */
     @Nullable
     public final <T> T readTypedObject(@NonNull Parcelable.Creator<T> c) {
+        assertNotRecycled();
         if (readInt() != 0) {
             return c.createFromParcel(this);
         } else {
@@ -4228,6 +4440,7 @@
      * @see #readTypedArray
      */
     public <T> void readFixedArray(@NonNull T val) {
+        assertNotRecycled();
         Class<?> componentType = val.getClass().getComponentType();
         if (componentType == boolean.class) {
             readBooleanArray((boolean[]) val);
@@ -4268,6 +4481,7 @@
      */
     public <T, S extends IInterface> void readFixedArray(@NonNull T val,
             @NonNull Function<IBinder, S> asInterface) {
+        assertNotRecycled();
         Class<?> componentType = val.getClass().getComponentType();
         if (IInterface.class.isAssignableFrom(componentType)) {
             readInterfaceArray((S[]) val, asInterface);
@@ -4294,6 +4508,7 @@
      */
     public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
             @NonNull Parcelable.Creator<S> c) {
+        assertNotRecycled();
         Class<?> componentType = val.getClass().getComponentType();
         if (Parcelable.class.isAssignableFrom(componentType)) {
             readTypedArray((S[]) val, c);
@@ -4351,6 +4566,7 @@
      */
     @Nullable
     public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
+        assertNotRecycled();
         // Check if type matches with dimensions
         // If type is one-dimensional array, delegate to other creators
         // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4424,6 +4640,7 @@
     @Nullable
     public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
             @NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
+        assertNotRecycled();
         // Check if type matches with dimensions
         // If type is one-dimensional array, delegate to other creators
         // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4484,6 +4701,7 @@
     @Nullable
     public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
             @NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
+        assertNotRecycled();
         // Check if type matches with dimensions
         // If type is one-dimensional array, delegate to other creators
         // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
@@ -4547,6 +4765,7 @@
      */
     public final <T extends Parcelable> void writeParcelableArray(@Nullable T[] value,
             int parcelableFlags) {
+        assertNotRecycled();
         if (value != null) {
             int N = value.length;
             writeInt(N);
@@ -4565,6 +4784,7 @@
      */
     @Nullable
     public final Object readValue(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         return readValue(loader, /* clazz */ null);
     }
 
@@ -4620,6 +4840,7 @@
      */
     @Nullable
     public Object readLazyValue(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         int start = dataPosition();
         int type = readInt();
         if (isLengthPrefixed(type)) {
@@ -5022,6 +5243,7 @@
     @Deprecated
     @Nullable
     public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         return readParcelableInternal(loader, /* clazz */ null);
     }
 
@@ -5041,6 +5263,7 @@
      */
     @Nullable
     public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        assertNotRecycled();
         Objects.requireNonNull(clazz);
         return readParcelableInternal(loader, clazz);
     }
@@ -5069,6 +5292,7 @@
     @Nullable
     public final <T extends Parcelable> T readCreator(@NonNull Parcelable.Creator<?> creator,
             @Nullable ClassLoader loader) {
+        assertNotRecycled();
         if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
           Parcelable.ClassLoaderCreator<?> classLoaderCreator =
               (Parcelable.ClassLoaderCreator<?>) creator;
@@ -5096,6 +5320,7 @@
     @Deprecated
     @Nullable
     public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         return readParcelableCreatorInternal(loader, /* clazz */ null);
     }
 
@@ -5116,6 +5341,7 @@
     @Nullable
     public <T> Parcelable.Creator<T> readParcelableCreator(
             @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        assertNotRecycled();
         Objects.requireNonNull(clazz);
         return readParcelableCreatorInternal(loader, clazz);
     }
@@ -5238,6 +5464,7 @@
     @Deprecated
     @Nullable
     public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         return readParcelableArrayInternal(loader, /* clazz */ null);
     }
 
@@ -5258,6 +5485,7 @@
     @SuppressLint({"ArrayReturn", "NullableCollection"})
     @Nullable
     public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        assertNotRecycled();
         return readParcelableArrayInternal(loader, requireNonNull(clazz));
     }
 
@@ -5291,6 +5519,7 @@
     @Deprecated
     @Nullable
     public Serializable readSerializable() {
+        assertNotRecycled();
         return readSerializableInternal(/* loader */ null, /* clazz */ null);
     }
 
@@ -5307,6 +5536,7 @@
      */
     @Nullable
     public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        assertNotRecycled();
         Objects.requireNonNull(clazz);
         return readSerializableInternal(
                 loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -5548,6 +5778,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
             @Nullable ClassLoader loader) {
+        assertNotRecycled();
         final int N = readInt();
         if (N < 0) {
             return;
@@ -5564,6 +5795,7 @@
      */
     @UnsupportedAppUsage
     public @Nullable ArraySet<? extends Object> readArraySet(@Nullable ClassLoader loader) {
+        assertNotRecycled();
         final int size = readInt();
         if (size < 0) {
             return null;
@@ -5703,6 +5935,7 @@
      * @hide For testing
      */
     public long getOpenAshmemSize() {
+        assertNotRecycled();
         return nativeGetOpenAshmemSize(mNativePtr);
     }
 
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index c2b8157..57e1b58 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -474,3 +474,12 @@
     description: "Enable cross-user roles platform API"
     bug: "367732307"
 }
+
+flag {
+    name: "rate_limit_batched_note_op_async_callbacks_enabled"
+    is_fixed_read_only: true
+    is_exported: true
+    namespace: "permissions"
+    description: "Rate limit async noteOp callbacks for batched noteOperation binder call"
+    bug: "366013082"
+}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 269839b..2d922b4 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -15,9 +15,12 @@
  */
 package android.service.autofill;
 
+import static android.service.autofill.Flags.FLAG_AUTOFILL_SESSION_DESTROYED;
+
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -669,6 +672,14 @@
                     AutofillService.this,
                     new SavedDatasetsInfoCallbackImpl(receiver, SavedDatasetsInfo.TYPE_PASSWORDS)));
         }
+
+        @Override
+        public void onSessionDestroyed(@Nullable FillEventHistory history) {
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onSessionDestroyed,
+                    AutofillService.this,
+                    history));
+        }
     };
 
     private Handler mHandler;
@@ -783,26 +794,42 @@
     }
 
     /**
-     * Gets the events that happened after the last
-     * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
+     * Called when an Autofill context has ended and the Autofill session is finished. This will be
+     * called as the last step of the Autofill lifecycle described in {@link AutofillManager}.
+     *
+     * <p>This will also contain the finished Session's FillEventHistory, so providers do not need
+     * to explicitly call {@link #getFillEventHistory()}
+     *
+     * <p>This will usually happens whenever {@link AutofillManager#commit()} or {@link
+     * AutofillManager#cancel()} is called.
+     */
+    @FlaggedApi(FLAG_AUTOFILL_SESSION_DESTROYED)
+    public void onSessionDestroyed(@Nullable FillEventHistory history) {}
+
+    /**
+     * Gets the events that happened after the last {@link
+     * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
      * call.
      *
      * <p>This method is typically used to keep track of previous user actions to optimize further
      * requests. For example, the service might return email addresses in alphabetical order by
      * default, but change that order based on the address the user picked on previous requests.
      *
-     * <p>The history is not persisted over reboots, and it's cleared every time the service
-     * replies to a {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} by calling
-     * {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)}
-     * (if the service doesn't call any of these methods, the history will clear out after some
-     * pre-defined time). Hence, the service should call {@link #getFillEventHistory()} before
-     * finishing the {@link FillCallback}.
+     * <p>The history is not persisted over reboots, and it's cleared every time the service replies
+     * to a {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} by calling {@link
+     * FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)} (if the
+     * service doesn't call any of these methods, the history will clear out after some pre-defined
+     * time). Hence, the service should call {@link #getFillEventHistory()} before finishing the
+     * {@link FillCallback}.
      *
      * @return The history or {@code null} if there are no events.
-     *
      * @throws RuntimeException if the event history could not be retrieved.
+     * @deprecated Use {@link #onSessionDestroyed(FillEventHistory) instead}
      */
-    @Nullable public final FillEventHistory getFillEventHistory() {
+    @FlaggedApi(FLAG_AUTOFILL_SESSION_DESTROYED)
+    @Deprecated
+    @Nullable
+    public final FillEventHistory getFillEventHistory() {
         final AutofillManager afm = getSystemService(AutofillManager.class);
 
         if (afm == null) {
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index 3b64b8a..71b75e7 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -25,6 +25,8 @@
 import android.service.autofill.SaveRequest;
 import com.android.internal.os.IResultReceiver;
 
+parcelable FillEventHistory;
+
 /**
  * Interface from the system to an auto fill service.
  *
@@ -38,4 +40,5 @@
     void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
     void onSavedPasswordCountRequest(in IResultReceiver receiver);
     void onConvertCredentialRequest(in ConvertCredentialRequest convertCredentialRequest, in IConvertCredentialCallback convertCredentialCallback);
+    void onSessionDestroyed(in FillEventHistory history);
 }
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index bb233d2..3ff5f95 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -286,7 +286,7 @@
      * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
      * timebase.
      * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
-     * @param modeId The new mode Id
+     * @param modeId The new mode ID
      * @param renderPeriod The render frame period, which is a multiple of the mode's vsync period
      */
     public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
@@ -294,6 +294,15 @@
     }
 
     /**
+     * Called when a display mode rejection event is received.
+     *
+     * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
+     * @param modeId The mode ID of the mode that was rejected
+     */
+    public void onModeRejected(long physicalDisplayId, int modeId) {
+    }
+
+    /**
      * Called when a display hdcp levels change event is received.
      *
      * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
@@ -386,6 +395,12 @@
 
     // Called from native code.
     @SuppressWarnings("unused")
+    private void dispatchModeRejected(long physicalDisplayId, int modeId) {
+        onModeRejected(physicalDisplayId, modeId);
+    }
+
+    // Called from native code.
+    @SuppressWarnings("unused")
     private void dispatchFrameRateOverrides(long timestampNanos, long physicalDisplayId,
             FrameRateOverride[] overrides) {
         onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides);
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index 641b010..f6fdec9 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -142,4 +142,12 @@
     description: "Recover from buffer stuffing when SurfaceFlinger misses a frame"
     bug: "294922229"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+    name: "date_time_view_relative_time_display_configs"
+    namespace: "systemui"
+    description: "Enables DateTimeView to use additional display configurations for relative time"
+    bug: "364653005"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 41ff69d..143b4b7 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -21,6 +21,7 @@
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.YEAR_IN_MILLIS;
 
+import android.annotation.IntDef;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
@@ -41,6 +42,8 @@
 
 import com.android.internal.R;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.text.DateFormat;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -70,6 +73,23 @@
     private static final int SHOW_TIME = 0;
     private static final int SHOW_MONTH_DAY_YEAR = 1;
 
+    /** @hide */
+    @IntDef(value = {UNIT_DISPLAY_LENGTH_SHORTEST, UNIT_DISPLAY_LENGTH_MEDIUM})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UnitDisplayLength {}
+    public static final int UNIT_DISPLAY_LENGTH_SHORTEST = 0;
+    public static final int UNIT_DISPLAY_LENGTH_MEDIUM = 1;
+
+    /** @hide */
+    @IntDef(flag = true, value = {DISAMBIGUATION_TEXT_PAST, DISAMBIGUATION_TEXT_FUTURE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DisambiguationTextMask {}
+    public static final int DISAMBIGUATION_TEXT_PAST = 0x01;
+    public static final int DISAMBIGUATION_TEXT_FUTURE = 0x02;
+
+    private final boolean mCanUseRelativeTimeDisplayConfigs =
+            android.view.flags.Flags.dateTimeViewRelativeTimeDisplayConfigs();
+
     private long mTimeMillis;
     // The LocalDateTime equivalent of mTimeMillis but truncated to minute, i.e. no seconds / nanos.
     private LocalDateTime mLocalTime;
@@ -81,6 +101,8 @@
     private static final ThreadLocal<ReceiverInfo> sReceiverInfo = new ThreadLocal<ReceiverInfo>();
     private String mNowText;
     private boolean mShowRelativeTime;
+    private int mRelativeTimeDisambiguationTextMask;
+    private int mRelativeTimeUnitDisplayLength = UNIT_DISPLAY_LENGTH_SHORTEST;
 
     public DateTimeView(Context context) {
         this(context, null);
@@ -89,20 +111,23 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public DateTimeView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                com.android.internal.R.styleable.DateTimeView, 0,
-                0);
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.DateTimeView, 0, 0);
 
-        final int N = a.getIndexCount();
-        for (int i = 0; i < N; i++) {
-            int attr = a.getIndex(i);
-            switch (attr) {
-                case R.styleable.DateTimeView_showRelative:
-                    boolean relative = a.getBoolean(i, false);
-                    setShowRelativeTime(relative);
-                    break;
-            }
+        setShowRelativeTime(a.getBoolean(R.styleable.DateTimeView_showRelative, false));
+        if (mCanUseRelativeTimeDisplayConfigs) {
+            setRelativeTimeDisambiguationTextMask(
+                    a.getInt(
+                            R.styleable.DateTimeView_relativeTimeDisambiguationText,
+                            // The original implementation showed disambiguation text for future
+                            // times only, so continue with that default.
+                            DISAMBIGUATION_TEXT_FUTURE));
+            setRelativeTimeUnitDisplayLength(
+                    a.getInt(
+                            R.styleable.DateTimeView_relativeTimeUnitDisplayLength,
+                            UNIT_DISPLAY_LENGTH_SHORTEST));
         }
+
         a.recycle();
     }
 
@@ -150,6 +175,29 @@
         update();
     }
 
+    /** See {@link R.styleable.DateTimeView_relativeTimeDisambiguationText}. */
+    @android.view.RemotableViewMethod
+    public void setRelativeTimeDisambiguationTextMask(
+            @DisambiguationTextMask int disambiguationTextMask) {
+        if (!mCanUseRelativeTimeDisplayConfigs) {
+            return;
+        }
+        mRelativeTimeDisambiguationTextMask = disambiguationTextMask;
+        updateNowText();
+        update();
+    }
+
+    /** See {@link R.styleable.DateTimeView_relativeTimeUnitDisplayLength}. */
+    @android.view.RemotableViewMethod
+    public void setRelativeTimeUnitDisplayLength(@UnitDisplayLength int unitDisplayLength) {
+        if (!mCanUseRelativeTimeDisplayConfigs) {
+            return;
+        }
+        mRelativeTimeUnitDisplayLength = unitDisplayLength;
+        updateNowText();
+        update();
+    }
+
     /**
      * Returns whether this view shows relative time
      *
@@ -264,17 +312,11 @@
             return;
         } else if (duration < HOUR_IN_MILLIS) {
             count = (int)(duration / MINUTE_IN_MILLIS);
-            result = getContext().getResources().getString(past
-                    ? com.android.internal.R.string.duration_minutes_shortest
-                    : com.android.internal.R.string.duration_minutes_shortest_future,
-                    count);
+            result = getContext().getResources().getString(getMinutesStringId(past), count);
             millisIncrease = MINUTE_IN_MILLIS;
         } else if (duration < DAY_IN_MILLIS) {
             count = (int)(duration / HOUR_IN_MILLIS);
-            result = getContext().getResources().getString(past
-                            ? com.android.internal.R.string.duration_hours_shortest
-                            : com.android.internal.R.string.duration_hours_shortest_future,
-                            count);
+            result = getContext().getResources().getString(getHoursStringId(past), count);
             millisIncrease = HOUR_IN_MILLIS;
         } else if (duration < YEAR_IN_MILLIS) {
             // In weird cases it can become 0 because of daylight savings
@@ -283,10 +325,7 @@
             LocalDateTime localNow = toLocalDateTime(now, zoneId);
 
             count = Math.max(Math.abs(dayDistance(localDateTime, localNow)), 1);
-            result = getContext().getResources().getString(past
-                    ? com.android.internal.R.string.duration_days_shortest
-                    : com.android.internal.R.string.duration_days_shortest_future,
-                    count);
+            result = getContext().getResources().getString(getDaysStringId(past), count);
             if (past || count != 1) {
                 mUpdateTimeMillis = computeNextMidnight(localNow, zoneId);
                 millisIncrease = -1;
@@ -296,10 +335,7 @@
 
         } else {
             count = (int)(duration / YEAR_IN_MILLIS);
-            result = getContext().getResources().getString(past
-                    ? com.android.internal.R.string.duration_years_shortest
-                    : com.android.internal.R.string.duration_years_shortest_future,
-                    count);
+            result = getContext().getResources().getString(getYearsStringId(past), count);
             millisIncrease = YEAR_IN_MILLIS;
         }
         if (millisIncrease != -1) {
@@ -312,6 +348,139 @@
         maybeSetText(result);
     }
 
+    private int getMinutesStringId(boolean past) {
+        if (!mCanUseRelativeTimeDisplayConfigs) {
+            return past
+                    ? com.android.internal.R.string.duration_minutes_shortest
+                    : com.android.internal.R.string.duration_minutes_shortest_future;
+        }
+
+        if (mRelativeTimeUnitDisplayLength == UNIT_DISPLAY_LENGTH_SHORTEST) {
+            if (past && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_PAST) != 0) {
+                // "1m ago"
+                return com.android.internal.R.string.duration_minutes_shortest_past;
+            } else if (!past
+                    && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_FUTURE) != 0) {
+                // "in 1m"
+                return com.android.internal.R.string.duration_minutes_shortest_future;
+            } else {
+                // "1m"
+                return com.android.internal.R.string.duration_minutes_shortest;
+            }
+        } else { // UNIT_DISPLAY_LENGTH_MEDIUM
+            if (past && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_PAST) != 0) {
+                // "1min ago"
+                return com.android.internal.R.string.duration_minutes_medium_past;
+            } else if (!past
+                    && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_FUTURE) != 0) {
+                // "in 1min"
+                return com.android.internal.R.string.duration_minutes_medium_future;
+            } else {
+                // "1min"
+                return com.android.internal.R.string.duration_minutes_medium;
+            }
+        }
+    }
+
+    private int getHoursStringId(boolean past) {
+        if (!mCanUseRelativeTimeDisplayConfigs) {
+            return past
+                    ? com.android.internal.R.string.duration_hours_shortest
+                    : com.android.internal.R.string.duration_hours_shortest_future;
+        }
+        if (mRelativeTimeUnitDisplayLength == UNIT_DISPLAY_LENGTH_SHORTEST) {
+            if (past && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_PAST) != 0) {
+                // "1h ago"
+                return com.android.internal.R.string.duration_hours_shortest_past;
+            } else if (!past
+                    && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_FUTURE) != 0) {
+                // "in 1h"
+                return com.android.internal.R.string.duration_hours_shortest_future;
+            } else {
+                // "1h"
+                return com.android.internal.R.string.duration_hours_shortest;
+            }
+        } else { // UNIT_DISPLAY_LENGTH_MEDIUM
+            if (past && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_PAST) != 0) {
+                // "1hr ago"
+                return com.android.internal.R.string.duration_hours_medium_past;
+            } else if (!past
+                    && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_FUTURE) != 0) {
+                // "in 1hr"
+                return com.android.internal.R.string.duration_hours_medium_future;
+            } else {
+                // "1hr"
+                return com.android.internal.R.string.duration_hours_medium;
+            }
+        }
+    }
+
+    private int getDaysStringId(boolean past) {
+        if (!mCanUseRelativeTimeDisplayConfigs) {
+            return past
+                    ? com.android.internal.R.string.duration_days_shortest
+                    : com.android.internal.R.string.duration_days_shortest_future;
+        }
+        if (mRelativeTimeUnitDisplayLength == UNIT_DISPLAY_LENGTH_SHORTEST) {
+            if (past && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_PAST) != 0) {
+                // "1d ago"
+                return com.android.internal.R.string.duration_days_shortest_past;
+            } else if (!past
+                    && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_FUTURE) != 0) {
+                // "in 1d"
+                return com.android.internal.R.string.duration_days_shortest_future;
+            } else {
+                // "1d"
+                return com.android.internal.R.string.duration_days_shortest;
+            }
+        } else { // UNIT_DISPLAY_LENGTH_MEDIUM
+            if (past && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_PAST) != 0) {
+                // "1d ago"
+                return com.android.internal.R.string.duration_days_medium_past;
+            } else if (!past
+                    && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_FUTURE) != 0) {
+                // "in 1d"
+                return com.android.internal.R.string.duration_days_medium_future;
+            } else {
+                // "1d"
+                return com.android.internal.R.string.duration_days_medium;
+            }
+        }
+    }
+
+    private int getYearsStringId(boolean past) {
+        if (!mCanUseRelativeTimeDisplayConfigs) {
+            return past
+                    ? com.android.internal.R.string.duration_years_shortest
+                    : com.android.internal.R.string.duration_years_shortest_future;
+        }
+        if (mRelativeTimeUnitDisplayLength == UNIT_DISPLAY_LENGTH_SHORTEST) {
+            if (past && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_PAST) != 0) {
+                // "1y ago"
+                return com.android.internal.R.string.duration_years_shortest_past;
+            } else if (!past
+                    && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_FUTURE) != 0) {
+                // "in 1y"
+                return com.android.internal.R.string.duration_years_shortest_future;
+            } else {
+                // "1y"
+                return com.android.internal.R.string.duration_years_shortest;
+            }
+        } else { // UNIT_DISPLAY_LENGTH_MEDIUM
+            if (past && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_PAST) != 0) {
+                // "1y ago"
+                return com.android.internal.R.string.duration_years_medium_past;
+            } else if (!past
+                    && (mRelativeTimeDisambiguationTextMask & DISAMBIGUATION_TEXT_FUTURE) != 0) {
+                // "in 1y"
+                return com.android.internal.R.string.duration_years_medium_future;
+            } else {
+                // "1y"
+                return com.android.internal.R.string.duration_years_medium;
+            }
+        }
+    }
+
     /**
      * Sets text only if the text has actually changed. This prevents needles relayouts of this
      * view when set to wrap_content.
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 2cfc680..f01aa80 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -163,4 +163,5 @@
     void finishOperationForDevice(IBinder clientId, int code, int uid, String packageName,
             @nullable String attributionTag, int virtualDeviceId);
    List<AppOpsManager.PackageOps> getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId);
+   oneway void noteOperationsInBatch(in Map batchedNoteOps);
 }
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index ee5bd65..644d699 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -70,6 +70,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.chooser.TargetInfo;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
@@ -352,6 +353,7 @@
         findViewById(R.id.use_same_profile_browser).setOnClickListener(v -> finish());
 
         findViewById(R.id.button_open).setOnClickListener(v -> {
+            TargetInfo.refreshIntentCreatorToken(launchIntent);
             startActivityAsCaller(
                     launchIntent,
                     ActivityOptions.makeCustomAnimation(
@@ -476,6 +478,7 @@
 
     private void startActivityAsCaller(Intent newIntent, int userId) {
         try {
+            TargetInfo.refreshIntentCreatorToken(newIntent);
             startActivityAsCaller(
                     newIntent,
                     /* options= */ null,
@@ -502,6 +505,7 @@
             return;
         }
         sanitizeIntent(innerIntent);
+        TargetInfo.refreshIntentCreatorToken(intentReceived);
         startActivityAsCaller(intentReceived, null, false, getUserId());
         finish();
     }
@@ -525,6 +529,7 @@
         if (singleTabOnly) {
             intentReceived.putExtra(EXTRA_RESTRICT_TO_SINGLE_USER, true);
         }
+        TargetInfo.refreshIntentCreatorToken(intentReceived);
         startActivityAsCaller(intentReceived, null, false, userId);
         finish();
     }
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 473134e..0c65077 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -173,6 +173,7 @@
     @Override
     public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
         TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
+        TargetInfo.refreshIntentCreatorToken(mResolvedIntent);
         activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
         return true;
     }
@@ -180,6 +181,7 @@
     @Override
     public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
         TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
+        TargetInfo.refreshIntentCreatorToken(mResolvedIntent);
         activity.startActivityAsUser(mResolvedIntent, options, user);
         return false;
     }
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index d7f3a76..0eaa43d 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -260,6 +260,7 @@
         intent.setComponent(mChooserTarget.getComponentName());
         intent.putExtras(mChooserTarget.getIntentExtras());
         TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId);
+        TargetInfo.refreshIntentCreatorToken(intent);
 
         // Important: we will ignore the target security checks in ActivityManager
         // if and only if the ChooserTarget's target package is the same package
diff --git a/core/java/com/android/internal/app/chooser/TargetInfo.java b/core/java/com/android/internal/app/chooser/TargetInfo.java
index 7bb7ddc..fcf5883 100644
--- a/core/java/com/android/internal/app/chooser/TargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/TargetInfo.java
@@ -17,13 +17,17 @@
 package com.android.internal.app.chooser;
 
 
+import static android.security.Flags.preventIntentRedirect;
+
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.os.UserHandle;
 
 import com.android.internal.app.ResolverActivity;
@@ -141,4 +145,20 @@
             intent.fixUris(currentUserId);
         }
     }
+
+    /**
+     * refreshes intent's creatorToken with its current intent key fields. This allows
+     * ChooserActivity to still keep original creatorToken's creator uid after making changes to
+     * the intent and still keep it valid.
+     * @param intent the intent's creatorToken needs to up refreshed.
+     */
+    static void refreshIntentCreatorToken(Intent intent) {
+        if (!preventIntentRedirect()) return;
+        try {
+            intent.setCreatorToken(ActivityManager.getService().refreshIntentCreatorToken(
+                    intent.cloneForCreatorToken()));
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 9b8551b..1e965c5d 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -85,51 +85,6 @@
     }
 
     /**
-     * This is like <code>new byte[length]</code>, but it allocates the array as non-movable. This
-     * prevents copies of the data from being left on the Java heap as a result of heap compaction.
-     * Use this when the array will contain sensitive data such as a password or cryptographic key
-     * that needs to be wiped from memory when no longer needed. The owner of the array is still
-     * responsible for the zeroization; {@link #zeroize(byte[])} should be used to do so.
-     *
-     * @param length the length of the array to allocate
-     * @return the new array
-     */
-    public static byte[] newNonMovableByteArray(int length) {
-        return (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, length);
-    }
-
-    /**
-     * Like {@link #newNonMovableByteArray(int)}, but allocates a char array.
-     *
-     * @param length the length of the array to allocate
-     * @return the new array
-     */
-    public static char[] newNonMovableCharArray(int length) {
-        return (char[]) VMRuntime.getRuntime().newNonMovableArray(char.class, length);
-    }
-
-    /**
-     * Zeroizes a byte array as securely as possible. Use this when the array contains sensitive
-     * data such as a password or cryptographic key.
-     * <p>
-     * This zeroizes the array in a way that is guaranteed to not be optimized out by the compiler.
-     * If supported by the architecture, it zeroizes the data not just in the L1 data cache but also
-     * in other levels of the memory hierarchy up to and including main memory (but not above that).
-     * <p>
-     * This works on any <code>byte[]</code>, but to ensure that copies of the array aren't left on
-     * the Java heap the array should have been allocated with {@link #newNonMovableByteArray(int)}.
-     * Use on other arrays might also introduce performance anomalies.
-     *
-     * @param array the array to zeroize
-     */
-    public static native void zeroize(byte[] array);
-
-    /**
-     * Like {@link #zeroize(byte[])}, but for char arrays.
-     */
-    public static native void zeroize(char[] array);
-
-    /**
      * Checks if the beginnings of two byte arrays are equal.
      *
      * @param array1 the first byte array
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0ffe2f9..e22d958 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -92,7 +92,6 @@
         "android_view_VelocityTracker.cpp",
         "android_view_VerifiedKeyEvent.cpp",
         "android_view_VerifiedMotionEvent.cpp",
-        "com_android_internal_util_ArrayUtils.cpp",
         "com_android_internal_util_VirtualRefBasePtr.cpp",
         "core_jni_helpers.cpp",
         ":deviceproductinfoconstants_aidl",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 78d69f0..ac187b0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -220,7 +220,6 @@
 extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
 extern int register_com_android_internal_security_VerityUtils(JNIEnv* env);
-extern int register_com_android_internal_util_ArrayUtils(JNIEnv* env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
 extern int register_android_window_WindowInfosListener(JNIEnv* env);
 extern int register_android_window_ScreenCapture(JNIEnv* env);
@@ -1622,7 +1621,6 @@
         REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
         REG_JNI(register_com_android_internal_os_ZygoteInit),
         REG_JNI(register_com_android_internal_security_VerityUtils),
-        REG_JNI(register_com_android_internal_util_ArrayUtils),
         REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
         REG_JNI(register_android_hardware_Camera),
         REG_JNI(register_android_hardware_camera2_CameraMetadata),
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index a09c405..7ff1f8c 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -40,6 +40,7 @@
     jmethodID dispatchHotplug;
     jmethodID dispatchHotplugConnectionError;
     jmethodID dispatchModeChanged;
+    jmethodID dispatchModeRejected;
     jmethodID dispatchFrameRateOverrides;
     jmethodID dispatchHdcpLevelsChanged;
 
@@ -95,6 +96,7 @@
     void dispatchHotplugConnectionError(nsecs_t timestamp, int errorCode) override;
     void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
                              nsecs_t renderPeriod) override;
+    void dispatchModeRejected(PhysicalDisplayId displayId, int32_t modeId) override;
     void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
                                     std::vector<FrameRateOverride> overrides) override;
     void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
@@ -271,6 +273,18 @@
     mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
 }
 
+void NativeDisplayEventReceiver::dispatchModeRejected(PhysicalDisplayId displayId, int32_t modeId) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+    ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
+    if (receiverObj.get()) {
+        ALOGV("receiver %p ~ Invoking Mode Rejected handler.", this);
+        env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchModeRejected,
+                            displayId.value, modeId);
+        ALOGV("receiver %p ~ Returned from Mode Rejected handler.", this);
+    }
+}
+
 void NativeDisplayEventReceiver::dispatchFrameRateOverrides(
         nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -405,6 +419,9 @@
     gDisplayEventReceiverClassInfo.dispatchModeChanged =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
                              "(JJIJ)V");
+    gDisplayEventReceiverClassInfo.dispatchModeRejected =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeRejected",
+                             "(JI)V");
     gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
                              "dispatchFrameRateOverrides",
diff --git a/core/jni/com_android_internal_util_ArrayUtils.cpp b/core/jni/com_android_internal_util_ArrayUtils.cpp
deleted file mode 100644
index c69e6c9..0000000
--- a/core/jni/com_android_internal_util_ArrayUtils.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "ArrayUtils"
-
-#include <android-base/logging.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <string.h>
-#include <unistd.h>
-#include <utils/Log.h>
-
-namespace android {
-
-static size_t GetCacheLineSize() {
-    long size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
-    if (size <= 0) {
-        ALOGE("Unable to determine L1 data cache line size. Assuming 32 bytes");
-        return 32;
-    }
-    return size;
-}
-
-#ifdef __aarch64__
-static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
-    // Execute 'dc cvac' at least once on each cache line in the memory region.
-    //
-    // 'dc cvac' stands for "Data Cache line Clean by Virtual Address to point-of-Coherency".
-    // It writes the cache line back to the "point-of-coherency", i.e. main memory.
-    //
-    // Since the memory region is not guaranteed to be cache-line-aligned, we use an "extra"
-    // instruction after the loop to make sure the last cache line gets covered.
-    for (size_t i = 0; i < size; i += cache_line_size) {
-        asm volatile("dc cvac, %0" ::"r"(p + i));
-    }
-    asm volatile("dc cvac, %0" ::"r"(p + size - 1));
-}
-#elif defined(__i386__) || defined(__x86_64__)
-static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
-    for (size_t i = 0; i < size; i += cache_line_size) {
-        asm volatile("clflush (%0)" ::"r"(p + i));
-    }
-    asm volatile("clflush (%0)" ::"r"(p + size - 1));
-}
-#elif defined(__riscv)
-static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
-    // This should eventually work, but it is not ready to be enabled yet:
-    //  1.) The Android emulator needs to add support for zicbom.
-    //  2.) Kernel needs to enable zicbom in usermode.
-    //  3.) Android clang needs to add zicbom to the target.
-#if 0
-    for (size_t i = 0; i < size; i += cache_line_size) {
-        asm volatile("cbo.clean (%0)" ::"r"(p + i));
-    }
-    asm volatile("cbo.clean (%0)" ::"r"(p + size - 1));
-#endif
-}
-#elif defined(__arm__)
-// arm32 has a cacheflush() syscall, but it is undocumented and only flushes the icache.
-// It is not the same as cacheflush(2) as documented in the Linux man-pages project.
-static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {}
-#else
-#error "Unknown architecture"
-#endif
-
-static void ZeroizePrimitiveArray(JNIEnv* env, jclass clazz, jarray array, size_t component_len) {
-    static const size_t cache_line_size = GetCacheLineSize();
-
-    size_t size = env->GetArrayLength(array) * component_len;
-    if (size == 0) {
-        return;
-    }
-
-    // ART guarantees that GetPrimitiveArrayCritical never copies.
-    jboolean isCopy;
-    void* elems = env->GetPrimitiveArrayCritical(array, &isCopy);
-    CHECK(!isCopy);
-
-#ifdef __BIONIC__
-    memset_explicit(elems, 0, size);
-#else
-    memset(elems, 0, size);
-#endif
-    // Clean the data cache so that the data gets zeroized in main memory right away.  Without this,
-    // it might not be written to main memory until the cache line happens to be evicted.
-    CleanDataCache(static_cast<const uint8_t*>(elems), size, cache_line_size);
-
-    env->ReleasePrimitiveArrayCritical(array, elems, /* mode= */ 0);
-}
-
-static void ZeroizeByteArray(JNIEnv* env, jclass clazz, jbyteArray array) {
-    ZeroizePrimitiveArray(env, clazz, array, sizeof(jbyte));
-}
-
-static void ZeroizeCharArray(JNIEnv* env, jclass clazz, jcharArray array) {
-    ZeroizePrimitiveArray(env, clazz, array, sizeof(jchar));
-}
-
-static const JNINativeMethod sMethods[] = {
-        {"zeroize", "([B)V", (void*)ZeroizeByteArray},
-        {"zeroize", "([C)V", (void*)ZeroizeCharArray},
-};
-
-int register_com_android_internal_util_ArrayUtils(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "com/android/internal/util/ArrayUtils", sMethods,
-                                    NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8c46ccc..238aca5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -10570,6 +10570,32 @@
 
     <declare-styleable name="DateTimeView">
         <attr name="showRelative" format="boolean" />
+        <!-- For relative times, controls what kinds of times get disambiguation text.
+
+             The default value is "future".
+
+             Does nothing if showRelative=false.
+             -->
+        <attr name="relativeTimeDisambiguationText">
+            <!-- Times in the past will have extra clarifying text indicating that the time is in
+                 the past. For example, 1 minute ago is represented as "1m ago". If this flag is not
+                 set, times in the past are represented as just "1m". -->
+            <flag name="past" value="0x01" />
+            <!-- Times in the future will have extra clarifying text indicating that the time is in
+                 the future. For example, 1 minute in the future is represented as "in 1m". If this
+                 flag is not set, times in the future are represented as just "1m". -->
+            <flag name="future" value="0x02" />
+        </attr>
+        <!-- For relative times, sets the length of the time unit displayed (minutes, hours, etc.).
+
+             Does nothing if showRelative=false.
+             -->
+        <attr name="relativeTimeUnitDisplayLength">
+            <!-- The time unit will be shown as a short as possible (1 character if possible). -->
+            <enum name="shortest" value="0" />
+            <!-- The time unit will be shortened to a medium length (2-3 characters in general). -->
+            <enum name="medium" value="1" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="ResolverDrawerLayout_LayoutParams">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a2b7de1..390fbea 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -815,6 +815,11 @@
          we rely on gravity to determine the effective orientation. -->
     <bool name="config_deskDockEnablesAccelerometer">true</bool>
 
+    <!-- Control for allowing the dock rotation functionality before provision like
+         when the SetupWizard is being shown to the user. This defaults to false to
+         preserve existing behavior. -->
+    <bool name="config_allowDockBeforeProvision">false</bool>
+
     <!-- Car dock behavior -->
 
     <!-- The number of degrees to rotate the display when the device is in a car dock.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d498b91..cfc3ddc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3135,6 +3135,86 @@
         in <xliff:g id="count">%d</xliff:g>y
     </string>
 
+    <!-- Phrase describing a time duration using minutes that is as short as possible, preferrably one character. This version should be a past point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=14] -->
+    <string name="duration_minutes_shortest_past">
+        <xliff:g id="count">%d</xliff:g>m ago
+    </string>
+
+    <!-- Phrase describing a time duration using hours that is as short as possible, preferrably one character. This version should be a past point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=14] -->
+    <string name="duration_hours_shortest_past">
+        <xliff:g id="count">%d</xliff:g>h ago
+    </string>
+
+    <!-- Phrase describing a time duration using days that is as short as possible, preferrably one character. This version should be a past point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=14] -->
+    <string name="duration_days_shortest_past">
+        <xliff:g example="1" id="count">%d</xliff:g>d ago
+    </string>
+
+    <!-- Phrase describing a time duration using years that is as short as possible, preferrably one character. This version should be a past point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=14] -->
+    <string name="duration_years_shortest_past">
+        <xliff:g id="count">%d</xliff:g>y ago
+    </string>
+
+    <!-- Phrase describing a time duration using minutes that is a medium length, preferrably two or three characters. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=8] -->
+    <string name="duration_minutes_medium">
+        <xliff:g id="count">%d</xliff:g>min
+    </string>
+
+    <!-- Phrase describing a time duration using hours that is a medium length, preferrably two or three characters. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=8] -->
+    <string name="duration_hours_medium">
+        <xliff:g id="count">%d</xliff:g>hr
+    </string>
+
+    <!-- Phrase describing a time duration using days that is a medium length, preferrably two or three characters. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=8] -->
+    <string name="duration_days_medium">
+       <xliff:g id="count">%d</xliff:g>d
+    </string>
+
+    <!-- Phrase describing a time duration using years that is a medium length, preferrably two or three characters. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=8] -->
+    <string name="duration_years_medium">
+        <xliff:g id="count">%d</xliff:g>yr
+    </string>
+
+    <!-- Phrase describing a time duration using minutes that is a medium length, preferrably two or three characters. This version should be a future point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=18] -->
+    <string name="duration_minutes_medium_future">
+        in <xliff:g id="count">%d</xliff:g>min
+    </string>
+
+    <!-- Phrase describing a time duration using hours that is a medium length, preferrably two or three characters. This version should be a future point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=18] -->
+    <string name="duration_hours_medium_future">
+        in <xliff:g id="count">%d</xliff:g>hr
+    </string>
+
+    <!-- Phrase describing a time duration using days that is a medium length, preferrably two or three characters. This version should be a future point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=18] -->
+    <string name="duration_days_medium_future">
+        in <xliff:g example="1" id="count">%d</xliff:g>d
+    </string>
+
+    <!-- Phrase describing a time duration using years that is a medium length, preferrably two or three characters. This version should be a future point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=18] -->
+    <string name="duration_years_medium_future">
+        in <xliff:g id="count">%d</xliff:g>yr
+    </string>
+
+    <!-- Phrase describing a time duration using minutes that is a medium length, preferrably two or three characters. This version should be a past point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=18] -->
+    <string name="duration_minutes_medium_past">
+        <xliff:g id="count">%d</xliff:g>min ago
+    </string>
+
+    <!-- Phrase describing a time duration using hours that is a medium length, preferrably two or three characters. This version should be a past point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=18] -->
+    <string name="duration_hours_medium_past">
+        <xliff:g id="count">%d</xliff:g>hr ago
+    </string>
+
+    <!-- Phrase describing a time duration using days that is a medium length, preferrably two or three characters. This version should be a past point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=18] -->
+    <string name="duration_days_medium_past">
+        <xliff:g example="1" id="count">%d</xliff:g>d ago
+    </string>
+
+    <!-- Phrase describing a time duration using years that is a medium length, preferrably two or three characters. This version should be a past point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=18] -->
+    <string name="duration_years_medium_past">
+        <xliff:g id="count">%d</xliff:g>yr ago
+    </string>
+
     <!-- Phrase describing a relative time using minutes in the past that is not shown on the screen but used for accessibility. [CHAR LIMIT=NONE] -->
     <string name="duration_minutes_relative">{count, plural,
         =1 {# minute ago}
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9a51b72..2671ff9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1797,6 +1797,7 @@
   <java-symbol type="bool" name="config_customUserSwitchUi" />
   <java-symbol type="bool" name="config_canRemoveFirstAccount" />
   <java-symbol type="string" name="config_accountTypeToKeepFirstAccount" />
+  <java-symbol type="bool" name="config_allowDockBeforeProvision" />
   <java-symbol type="bool" name="config_deskDockEnablesAccelerometer" />
   <java-symbol type="bool" name="config_disableMenuKeyInLockScreen" />
   <java-symbol type="bool" name="config_enableCarDockHomeLaunch" />
@@ -3367,6 +3368,23 @@
   <java-symbol type="string" name="duration_hours_shortest_future" />
   <java-symbol type="string" name="duration_days_shortest_future" />
   <java-symbol type="string" name="duration_years_shortest_future" />
+  <java-symbol type="string" name="duration_minutes_shortest_past" />
+  <java-symbol type="string" name="duration_hours_shortest_past" />
+  <java-symbol type="string" name="duration_days_shortest_past" />
+  <java-symbol type="string" name="duration_years_shortest_past" />
+
+  <java-symbol type="string" name="duration_minutes_medium" />
+  <java-symbol type="string" name="duration_hours_medium" />
+  <java-symbol type="string" name="duration_days_medium" />
+  <java-symbol type="string" name="duration_years_medium" />
+  <java-symbol type="string" name="duration_minutes_medium_future" />
+  <java-symbol type="string" name="duration_hours_medium_future" />
+  <java-symbol type="string" name="duration_days_medium_future" />
+  <java-symbol type="string" name="duration_years_medium_future" />
+  <java-symbol type="string" name="duration_minutes_medium_past" />
+  <java-symbol type="string" name="duration_hours_medium_past" />
+  <java-symbol type="string" name="duration_days_medium_past" />
+  <java-symbol type="string" name="duration_years_medium_past" />
 
   <java-symbol type="string" name="duration_minutes_relative" />
   <java-symbol type="string" name="duration_hours_relative" />
diff --git a/core/tests/coretests/src/android/widget/DateTimeViewTest.java b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
index a8fd913..be65277 100644
--- a/core/tests/coretests/src/android/widget/DateTimeViewTest.java
+++ b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
@@ -69,6 +69,141 @@
         Assert.assertFalse(dateTimeView.wasLayoutRequested());
     }
 
+    @UiThreadTest
+    @Test
+    public void disambiguationTextMask_none_noPastOrFutureDisambiguationText() {
+        final TestDateTimeView dateTimeView = new TestDateTimeView();
+        dateTimeView.setShowRelativeTime(true);
+        dateTimeView.setRelativeTimeDisambiguationTextMask(0);
+
+        // Minutes
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofMinutes(8).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("in"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofMinutes(8).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("ago"));
+
+        // Hours
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofHours(4).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("in"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofHours(4).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("ago"));
+
+        // Days
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofDays(14).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("in"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofDays(14).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("ago"));
+
+        // Years
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofDays(400).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("in"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofDays(400).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("ago"));
+    }
+
+    @UiThreadTest
+    @Test
+    public void disambiguationTextMask_bothPastAndFuture_usesPastAndFutureDisambiguationText() {
+        final TestDateTimeView dateTimeView = new TestDateTimeView();
+        dateTimeView.setShowRelativeTime(true);
+        dateTimeView.setRelativeTimeDisambiguationTextMask(
+                DateTimeView.DISAMBIGUATION_TEXT_PAST | DateTimeView.DISAMBIGUATION_TEXT_FUTURE);
+
+        // Minutes
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofMinutes(8).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("in"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofMinutes(8).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("ago"));
+
+        // Hours
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofHours(4).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("in"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofHours(4).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("ago"));
+
+        // Days
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofDays(14).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("in"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofDays(14).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("ago"));
+
+        // Years
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofDays(400).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("in"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofDays(400).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("ago"));
+    }
+
+    @UiThreadTest
+    @Test
+    public void unitDisplayLength_shortest_noMediumText() {
+        final TestDateTimeView dateTimeView = new TestDateTimeView();
+        dateTimeView.setShowRelativeTime(true);
+        dateTimeView.setRelativeTimeUnitDisplayLength(DateTimeView.UNIT_DISPLAY_LENGTH_SHORTEST);
+
+        // Minutes
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofMinutes(8).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("min"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofMinutes(8).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("min"));
+
+        // Hours
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofHours(4).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("hr"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofHours(4).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("hr"));
+
+        // Days excluded because the string is the same for both shortest length and medium length
+
+        // Years
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofDays(400).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("yr"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofDays(400).toMillis());
+        Assert.assertFalse(dateTimeView.getText().toString().contains("yr"));
+    }
+
+    @UiThreadTest
+    @Test
+    public void unitDisplayLength_medium_usesMediumText() {
+        final TestDateTimeView dateTimeView = new TestDateTimeView();
+        dateTimeView.setShowRelativeTime(true);
+        dateTimeView.setRelativeTimeUnitDisplayLength(DateTimeView.UNIT_DISPLAY_LENGTH_MEDIUM);
+
+        // Minutes
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofMinutes(8).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("min"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofMinutes(8).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("min"));
+
+        // Hours
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofHours(4).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("hr"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofHours(4).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("hr"));
+
+        // Days excluded because the string is the same for both shortest length and medium length
+
+        // Years
+        dateTimeView.setTime(System.currentTimeMillis() + Duration.ofDays(400).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("yr"));
+
+        dateTimeView.setTime(System.currentTimeMillis() - Duration.ofDays(400).toMillis());
+        Assert.assertTrue(dateTimeView.getText().toString().contains("yr"));
+    }
+
     private static class TestDateTimeView extends DateTimeView {
         private boolean mRequestedLayout = false;
 
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index 3c264f1..3b9f35b 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -496,51 +496,4 @@
             // expected
         }
     }
-
-    // Note: the zeroize() tests only test the behavior that can be tested from a Java test.
-    // They do not verify that no copy of the data is left anywhere.
-
-    @Test
-    @SmallTest
-    public void testZeroizeNonMovableByteArray() {
-        final int length = 10;
-        byte[] array = ArrayUtils.newNonMovableByteArray(length);
-        assertArrayEquals(array, new byte[length]);
-        Arrays.fill(array, (byte) 0xff);
-        ArrayUtils.zeroize(array);
-        assertArrayEquals(array, new byte[length]);
-    }
-
-    @Test
-    @SmallTest
-    public void testZeroizeRegularByteArray() {
-        final int length = 10;
-        byte[] array = new byte[length];
-        assertArrayEquals(array, new byte[length]);
-        Arrays.fill(array, (byte) 0xff);
-        ArrayUtils.zeroize(array);
-        assertArrayEquals(array, new byte[length]);
-    }
-
-    @Test
-    @SmallTest
-    public void testZeroizeNonMovableCharArray() {
-        final int length = 10;
-        char[] array = ArrayUtils.newNonMovableCharArray(length);
-        assertArrayEquals(array, new char[length]);
-        Arrays.fill(array, (char) 0xff);
-        ArrayUtils.zeroize(array);
-        assertArrayEquals(array, new char[length]);
-    }
-
-    @Test
-    @SmallTest
-    public void testZeroizeRegularCharArray() {
-        final int length = 10;
-        char[] array = new char[length];
-        assertArrayEquals(array, new char[length]);
-        Arrays.fill(array, (char) 0xff);
-        ArrayUtils.zeroize(array);
-        assertArrayEquals(array, new char[length]);
-    }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 897fc54..a26f5e3 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -514,7 +514,6 @@
         <permission name="android.permission.RENOUNCE_PERMISSIONS" />
         <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
         <permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
-        <permission name="android.permission.READ_DROPBOX_DATA" />
         <permission name="android.permission.READ_LOGS" />
         <permission name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
         <permission name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 073307c..d010c52 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -301,10 +301,7 @@
      *
      * @param bounds Returns the computed bounds of the path's control points.
      * @param exact This parameter is no longer used.
-     *
-     * @deprecated use computeBounds(RectF) instead
      */
-    @Deprecated
     @SuppressWarnings({"UnusedDeclaration"})
     public void computeBounds(@NonNull RectF bounds, boolean exact) {
         computeBounds(bounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 032dac9..37d5878 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -1317,9 +1317,6 @@
                 // otherwise a new transition will notify the relevant observers
                 if (returningToApp && allAppsAreTranslucent(mPausingTasks)) {
                     mHomeTransitionObserver.notifyHomeVisibilityChanged(true);
-                } else if (!toHome && mState == STATE_NEW_TASK
-                        && allAppsAreTranslucent(mOpeningTasks)) {
-                    // We are opening a translucent app. Launcher is still visible so we do nothing.
                 } else if (!toHome) {
                     // For some transitions, we may have notified home activity that it became
                     // visible. We need to notify the observer that we are no longer going home.
diff --git a/native/android/display_luts.cpp b/native/android/display_luts.cpp
index 179a32b..b03a718 100644
--- a/native/android/display_luts.cpp
+++ b/native/android/display_luts.cpp
@@ -26,8 +26,9 @@
 #define CHECK_NOT_NULL(name) \
     LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
 
-ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length, int32_t dimension,
-                                                 int32_t key) {
+ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length,
+                                                 ADisplayLuts_Dimension dimension,
+                                                 ADisplayLuts_SamplingKey key) {
     CHECK_NOT_NULL(buffer);
     LOG_ALWAYS_FATAL_IF(length >= ADISPLAYLUTS_BUFFER_LENGTH_LIMIT,
                         "the lut raw buffer length is too big to handle");
@@ -64,7 +65,7 @@
 
 ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* entry) {
     CHECK_NOT_NULL(entry);
-    return static_cast<ADisplayLuts_Dimension>(entry->properties.dimension);
+    return entry->properties.dimension;
 }
 
 int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) {
@@ -74,7 +75,7 @@
 
 ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* entry) {
     CHECK_NOT_NULL(entry);
-    return static_cast<ADisplayLuts_SamplingKey>(entry->properties.samplingKey);
+    return entry->properties.samplingKey;
 }
 
 const float* ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry) {
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 6bca145..4fe0b80 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -64,6 +64,8 @@
               static_cast<int>(android::gui::LutProperties::SamplingKey::RGB));
 static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB) ==
               static_cast<int>(android::gui::LutProperties::SamplingKey::MAX_RGB));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_CIE_Y) ==
+              static_cast<int>(android::gui::LutProperties::SamplingKey::CIE_Y));
 
 Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
     return reinterpret_cast<Transaction*>(aSurfaceTransaction);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0ec5571..fa6e2db 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -65,7 +65,6 @@
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
     <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
     <uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
-    <uses-permission android:name="android.permission.READ_DROPBOX_DATA" />
     <uses-permission android:name="android.permission.READ_LOGS" />
     <uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
     <uses-permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index e2f28fa..e445884 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -427,6 +427,18 @@
     }
 }
 
+
+flag {
+    name: "status_bar_chips_modernization"
+    namespace: "systemui"
+    description: "Deprecate OngoingCallController and implement OngoingActivityChips"
+    "in compose"
+    bug: "372657935"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
 flag {
     name: "status_bar_use_repos_for_call_chip"
     namespace: "systemui"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index c287da8..407bdf8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -662,7 +662,7 @@
     val standardAddShortcutRequest =
         ShortcutCustomizationRequestInfo.Add(
             label = "Standard shortcut",
-            categoryType = ShortcutCategoryType.System,
+            categoryType = System,
             subCategoryLabel = "Standard subcategory",
         )
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
index 2d05ee0..755c218 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
@@ -108,7 +108,7 @@
             viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
 
-            assertThat(uiState).isEqualTo(ResetShortcutDialog())
+            assertThat(uiState).isEqualTo(ResetShortcutDialog)
         }
     }
 
@@ -118,45 +118,7 @@
             viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
 
-            assertThat(uiState).isEqualTo(DeleteShortcutDialog())
-        }
-    }
-
-    @Test
-    fun uiState_consumedOnAddDialogShown() {
-        testScope.runTest {
-            val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
-            viewModel.onDialogShown()
-
-            assertThat((uiState as AddShortcutDialog).isDialogShowing)
-                .isTrue()
-        }
-    }
-
-    @Test
-    fun uiState_consumedOnDeleteDialogShown() {
-        testScope.runTest {
-            val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
-            viewModel.onDialogShown()
-
-            assertThat(
-                    (uiState as DeleteShortcutDialog).isDialogShowing
-                )
-                .isTrue()
-        }
-    }
-
-    @Test
-    fun uiState_consumedOnResetDialogShown() {
-        testScope.runTest {
-            val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
-            viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
-            viewModel.onDialogShown()
-
-            assertThat((uiState as ResetShortcutDialog).isDialogShowing)
-                .isTrue()
+            assertThat(uiState).isEqualTo(DeleteShortcutDialog)
         }
     }
 
@@ -165,7 +127,6 @@
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
             viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
-            viewModel.onDialogShown()
             viewModel.onDialogDismissed()
             assertThat(uiState).isEqualTo(ShortcutCustomizationUiState.Inactive)
         }
@@ -199,7 +160,6 @@
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
             viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
-            viewModel.onDialogShown()
 
             assertThat((uiState as AddShortcutDialog).errorMessage)
                 .isEmpty()
@@ -339,7 +299,6 @@
 
     private suspend fun openAddShortcutDialogAndSetShortcut() {
         viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
-        viewModel.onDialogShown()
 
         viewModel.onKeyPressed(keyDownEventWithActionKeyPressed)
         viewModel.onKeyPressed(keyUpEventWithActionKeyPressed)
@@ -349,14 +308,12 @@
 
     private suspend fun openDeleteShortcutDialogAndDeleteShortcut() {
         viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
-        viewModel.onDialogShown()
 
         viewModel.deleteShortcutCurrentlyBeingCustomized()
     }
 
     private suspend fun openResetShortcutDialogAndResetAllCustomShortcuts() {
         viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
-        viewModel.onDialogShown()
 
         viewModel.resetAllCustomShortcuts()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
similarity index 83%
rename from packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 3bd2496..78fce27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -18,6 +18,13 @@
 
 import android.app.role.RoleManager
 import android.app.role.mockRoleManager
+import android.content.Context
+import android.content.Context.INPUT_SERVICE
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.hardware.input.InputGestureData
+import android.hardware.input.fakeInputManager
 import android.view.KeyEvent
 import android.view.KeyboardShortcutGroup
 import android.view.KeyboardShortcutInfo
@@ -31,6 +38,7 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
@@ -56,7 +64,6 @@
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.settings.userTracker
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -64,6 +71,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -73,6 +84,9 @@
     private val fakeSystemSource = FakeKeyboardShortcutGroupsSource()
     private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
     private val fakeCurrentAppsSource = FakeKeyboardShortcutGroupsSource()
+    private val mockPackageManager: PackageManager = mock()
+    private val mockUserContext: Context = mock()
+    private val mockApplicationInfo: ApplicationInfo = mock()
 
     private val kosmos =
         Kosmos().also {
@@ -83,7 +97,7 @@
             it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperCurrentAppShortcutsSource = fakeCurrentAppsSource
-            it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { context })
+            it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
         }
 
     private val testScope = kosmos.testScope
@@ -91,13 +105,20 @@
     private val sysUiState = kosmos.sysUiState
     private val fakeUserTracker = kosmos.fakeUserTracker
     private val mockRoleManager = kosmos.mockRoleManager
+    private val inputManager = kosmos.fakeInputManager.inputManager
     private val viewModel = kosmos.shortcutHelperViewModel
 
+
     @Before
     fun setUp() {
         fakeSystemSource.setGroups(TestShortcuts.systemGroups)
         fakeMultiTaskingSource.setGroups(TestShortcuts.multitaskingGroups)
         fakeCurrentAppsSource.setGroups(TestShortcuts.currentAppGroups)
+        whenever(mockPackageManager.getApplicationInfo(anyString(), eq(0))).thenReturn(mockApplicationInfo)
+        whenever(mockPackageManager.getApplicationLabel(mockApplicationInfo)).thenReturn("Current App")
+        whenever(mockPackageManager.getApplicationIcon(anyString())).thenThrow(NameNotFoundException())
+        whenever(mockUserContext.packageManager).thenReturn(mockPackageManager)
+        whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
     }
 
     @Test
@@ -259,11 +280,11 @@
     fun shortcutsUiState_currentAppIsLauncher_defaultSelectedCategoryIsSystem() =
         testScope.runTest {
             whenever(
-                    mockRoleManager.getRoleHoldersAsUser(
-                        RoleManager.ROLE_HOME,
-                        fakeUserTracker.userHandle,
-                    )
+                mockRoleManager.getRoleHoldersAsUser(
+                    RoleManager.ROLE_HOME,
+                    fakeUserTracker.userHandle,
                 )
+            )
                 .thenReturn(listOf(TestShortcuts.currentAppPackageName))
             val uiState by collectLastValue(viewModel.shortcutsUiState)
 
@@ -299,23 +320,23 @@
                         label = "System",
                         iconSource = IconSource(imageVector = Icons.Default.Tv),
                         shortcutCategory =
-                            ShortcutCategory(
-                                System,
-                                subCategoryWithShortcutLabels("first Foo shortcut1"),
-                                subCategoryWithShortcutLabels(
-                                    "second foO shortcut2",
-                                    subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
-                                ),
+                        ShortcutCategory(
+                            System,
+                            subCategoryWithShortcutLabels("first Foo shortcut1"),
+                            subCategoryWithShortcutLabels(
+                                "second foO shortcut2",
+                                subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL,
                             ),
+                        ),
                     ),
                     ShortcutCategoryUi(
                         label = "Multitasking",
                         iconSource = IconSource(imageVector = Icons.Default.VerticalSplit),
                         shortcutCategory =
-                            ShortcutCategory(
-                                MultiTasking,
-                                subCategoryWithShortcutLabels("third FoO shortcut1"),
-                            ),
+                        ShortcutCategory(
+                            MultiTasking,
+                            subCategoryWithShortcutLabels("third FoO shortcut1"),
+                        ),
                     ),
                 )
         }
@@ -387,6 +408,31 @@
             assertThat(activeUiState.defaultSelectedCategory).isInstanceOf(CurrentApp::class.java)
         }
 
+    @Test
+    fun shortcutsUiState_shouldShowResetButton_isFalseWhenThereAreNoCustomShortcuts() =
+        testScope.runTest {
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            testHelper.showFromActivity()
+
+            val activeUiState = uiState as ShortcutsUiState.Active
+            assertThat(activeUiState.shouldShowResetButton).isFalse()
+        }
+
+    @Test
+    fun shortcutsUiState_shouldShowResetButton_isTrueWhenThereAreCustomShortcuts() =
+        testScope.runTest {
+            whenever(
+                inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+            ).thenReturn(listOf(allAppsInputGestureData))
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            testHelper.showFromActivity()
+
+            val activeUiState = uiState as ShortcutsUiState.Active
+            assertThat(activeUiState.shouldShowResetButton).isTrue()
+        }
+
     private fun groupWithShortcutLabels(
         vararg shortcutLabels: String,
         groupLabel: String = FIRST_SIMPLE_GROUP_LABEL,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index 9f84e34..f33de4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -128,7 +128,7 @@
                 viewModel,
                 dialogManager,
                 wifiStateWorker,
-                accessPointController
+                accessPointController,
             )
 
         underTest.initialize()
@@ -156,10 +156,7 @@
         testScope.runTest {
             connectivityRepository.defaultConnections.value = DefaultConnectionModel()
             wifiRepository.wifiScanResults.value =
-                listOf(
-                    WifiScanEntry(ssid = "ssid 1"),
-                    WifiScanEntry(ssid = "ssid 2"),
-                )
+                listOf(WifiScanEntry(ssid = "ssid 1"), WifiScanEntry(ssid = "ssid 2"))
 
             runCurrent()
             looper.processAllMessages()
@@ -204,10 +201,7 @@
         testScope.runTest {
             airplaneModeRepository.setIsAirplaneMode(true)
             connectivityRepository.defaultConnections.value =
-                DefaultConnectionModel(
-                    wifi = Wifi(true),
-                    isValidated = true,
-                )
+                DefaultConnectionModel(wifi = Wifi(true), isValidated = true)
             wifiRepository.setIsWifiEnabled(true)
             wifiRepository.setWifiNetwork(ACTIVE_WIFI)
 
@@ -222,10 +216,7 @@
     fun wifiConnected() =
         testScope.runTest {
             connectivityRepository.defaultConnections.value =
-                DefaultConnectionModel(
-                    wifi = Wifi(true),
-                    isValidated = true,
-                )
+                DefaultConnectionModel(wifi = Wifi(true), isValidated = true)
 
             wifiRepository.setIsWifiEnabled(true)
             wifiRepository.setWifiNetwork(ACTIVE_WIFI)
@@ -242,6 +233,7 @@
         whenever(wifiStateWorker.isWifiEnabled).thenReturn(true)
 
         underTest.secondaryClick(null)
+        looper.processAllMessages()
 
         verify(wifiStateWorker, times(1)).isWifiEnabled = eq(false)
     }
@@ -251,6 +243,7 @@
         whenever(wifiStateWorker.isWifiEnabled).thenReturn(false)
 
         underTest.secondaryClick(null)
+        looper.processAllMessages()
 
         verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true)
     }
@@ -258,10 +251,6 @@
     companion object {
         const val WIFI_SSID = "test ssid"
         val ACTIVE_WIFI =
-            WifiNetworkModel.Active.of(
-                isValidated = true,
-                level = 4,
-                ssid = WIFI_SSID,
-            )
+            WifiNetworkModel.Active.of(isValidated = true, level = 4, ssid = WIFI_SSID)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index 0cf9604..b5ec0a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -180,6 +180,7 @@
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
 
         mTile.secondaryClick(null);
+        mTestableLooper.processAllMessages();
 
         verify(mWifiStateWorker, times(1)).setWifiEnabled(eq(false));
     }
@@ -189,6 +190,7 @@
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
 
         mTile.secondaryClick(null);
+        mTestableLooper.processAllMessages();
 
         verify(mWifiStateWorker, times(1)).setWifiEnabled(eq(true));
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
index 7fed47a..e96dd16 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
@@ -42,7 +42,8 @@
     fun notificationChip_startsWithStartingModel() =
         kosmos.runTest {
             val icon = mock<StatusBarIconView>()
-            val startingNotif = activeNotificationModel(key = "notif1", statusBarChipIcon = icon)
+            val startingNotif =
+                activeNotificationModel(key = "notif1", statusBarChipIcon = icon, whenTime = 5432)
 
             val underTest = factory.create(startingNotif)
 
@@ -50,6 +51,7 @@
 
             assertThat(latest!!.key).isEqualTo("notif1")
             assertThat(latest!!.statusBarChipIconView).isEqualTo(icon)
+            assertThat(latest!!.whenTime).isEqualTo(5432)
         }
 
     @Test
@@ -65,11 +67,16 @@
 
             val newIconView = mock<StatusBarIconView>()
             underTest.setNotification(
-                activeNotificationModel(key = "notif1", statusBarChipIcon = newIconView)
+                activeNotificationModel(
+                    key = "notif1",
+                    statusBarChipIcon = newIconView,
+                    whenTime = 6543,
+                )
             )
 
             assertThat(latest!!.key).isEqualTo("notif1")
             assertThat(latest!!.statusBarChipIconView).isEqualTo(newIconView)
+            assertThat(latest!!.whenTime).isEqualTo(6543)
         }
 
     @Test
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 16376c5..a09cb69 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
@@ -101,7 +101,7 @@
 
             assertThat(latest).hasSize(1)
             val chip = latest!![0]
-            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
             assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
         }
 
@@ -171,7 +171,8 @@
 
     companion object {
         fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+            assertThat(latest)
+                .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
                 .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
deleted file mode 100644
index 3bd12e6..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2018 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.phone;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.platform.test.annotations.DisableFlags;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeHeadsUpTracker;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor;
-import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
-import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Optional;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper
-public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
-
-    private final NotificationStackScrollLayoutController mStackScrollerController =
-            mock(NotificationStackScrollLayoutController.class);
-    private final ShadeViewController mShadeViewController =
-            mock(ShadeViewController.class);
-    private final ShadeHeadsUpTracker mShadeHeadsUpTracker = mock(ShadeHeadsUpTracker.class);
-    private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
-    private HeadsUpAppearanceController mHeadsUpAppearanceController;
-    private NotificationTestHelper mTestHelper;
-    private ExpandableNotificationRow mRow;
-    private NotificationEntry mEntry;
-    private HeadsUpStatusBarView mHeadsUpStatusBarView;
-    private HeadsUpManager mHeadsUpManager;
-    private View mOperatorNameView;
-    private StatusBarStateController mStatusbarStateController;
-    private PhoneStatusBarTransitions mPhoneStatusBarTransitions;
-    private KeyguardBypassController mBypassController;
-    private NotificationWakeUpCoordinator mWakeUpCoordinator;
-    private KeyguardStateController mKeyguardStateController;
-    private CommandQueue mCommandQueue;
-    private NotificationRoundnessManager mNotificationRoundnessManager;
-
-    @Before
-    public void setUp() throws Exception {
-        allowTestableLooperAsMainThread();
-        mTestHelper = new NotificationTestHelper(
-                mContext,
-                mDependency,
-                TestableLooper.get(this));
-        mRow = mTestHelper.createRow();
-        mEntry = mRow.getEntry();
-        mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
-                mock(TextView.class));
-        mHeadsUpManager = mock(HeadsUpManager.class);
-        mOperatorNameView = new View(mContext);
-        mStatusbarStateController = mock(StatusBarStateController.class);
-        mPhoneStatusBarTransitions = mock(PhoneStatusBarTransitions.class);
-        mBypassController = mock(KeyguardBypassController.class);
-        mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class);
-        mKeyguardStateController = mock(KeyguardStateController.class);
-        mCommandQueue = mock(CommandQueue.class);
-        mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
-        when(mShadeViewController.getShadeHeadsUpTracker()).thenReturn(mShadeHeadsUpTracker);
-        mHeadsUpAppearanceController = new HeadsUpAppearanceController(
-                mHeadsUpManager,
-                mStatusbarStateController,
-                mPhoneStatusBarTransitions,
-                mBypassController,
-                mWakeUpCoordinator,
-                mDarkIconDispatcher,
-                mKeyguardStateController,
-                mCommandQueue,
-                mStackScrollerController,
-                mShadeViewController,
-                mNotificationRoundnessManager,
-                mHeadsUpStatusBarView,
-                new Clock(mContext, null),
-                mock(HeadsUpNotificationIconInteractor.class),
-                Optional.of(mOperatorNameView));
-        mHeadsUpAppearanceController.setAppearFraction(0.0f, 0.0f);
-    }
-
-    @Test
-    public void testShowinEntryUpdated() {
-        mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
-        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
-        when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
-        mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
-        assertEquals(mRow.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
-
-        mRow.setPinnedStatus(PinnedStatus.NotPinned);
-        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
-        assertNull(mHeadsUpStatusBarView.getShowingEntry());
-    }
-
-    @Test
-    public void testPinnedStatusUpdated() {
-        mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
-        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
-        when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
-        mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
-        assertEquals(PinnedStatus.PinnedBySystem, mHeadsUpAppearanceController.getPinnedStatus());
-
-        mRow.setPinnedStatus(PinnedStatus.NotPinned);
-        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
-        assertEquals(PinnedStatus.NotPinned, mHeadsUpAppearanceController.getPinnedStatus());
-    }
-
-    @Test
-    @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
-    public void testHeaderUpdated() {
-        mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
-        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
-        when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
-        mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
-        assertEquals(mRow.getHeaderVisibleAmount(), 0.0f, 0.0f);
-
-        mRow.setPinnedStatus(PinnedStatus.NotPinned);
-        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
-        assertEquals(mRow.getHeaderVisibleAmount(), 1.0f, 0.0f);
-    }
-
-    @Test
-    public void testOperatorNameViewUpdated() {
-        mHeadsUpAppearanceController.setAnimationsEnabled(false);
-
-        mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
-        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
-        when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
-        mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
-        assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility());
-
-        mRow.setPinnedStatus(PinnedStatus.NotPinned);
-        when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
-        assertEquals(View.VISIBLE, mOperatorNameView.getVisibility());
-    }
-
-    @Test
-    public void constructor_animationValuesUpdated() {
-        float appearFraction = .75f;
-        float expandedHeight = 400f;
-        when(mStackScrollerController.getAppearFraction()).thenReturn(appearFraction);
-        when(mStackScrollerController.getExpandedHeight()).thenReturn(expandedHeight);
-
-        HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
-                mHeadsUpManager,
-                mStatusbarStateController,
-                mPhoneStatusBarTransitions,
-                mBypassController,
-                mWakeUpCoordinator,
-                mDarkIconDispatcher,
-                mKeyguardStateController,
-                mCommandQueue,
-                mStackScrollerController,
-                mShadeViewController,
-                mNotificationRoundnessManager,
-                mHeadsUpStatusBarView,
-                new Clock(mContext, null),
-                mock(HeadsUpNotificationIconInteractor.class),
-                Optional.empty());
-
-        assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f);
-        assertEquals(appearFraction, newController.mAppearFraction, 0.0f);
-    }
-
-    @Test
-    public void testDestroy() {
-        reset(mHeadsUpManager);
-        reset(mDarkIconDispatcher);
-        reset(mShadeHeadsUpTracker);
-        reset(mStackScrollerController);
-
-        mHeadsUpAppearanceController.onViewDetached();
-
-        verify(mHeadsUpManager).removeListener(any());
-        verify(mDarkIconDispatcher).removeDarkReceiver(any());
-        verify(mShadeHeadsUpTracker).removeTrackingHeadsUpListener(any());
-        verify(mShadeHeadsUpTracker).setHeadsUpAppearanceController(isNull());
-        verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any());
-    }
-
-    @Test
-    public void testPulsingRoundness_onUpdateHeadsUpAndPulsingRoundness() {
-        // Pulsing: Enable flag and dozing
-        when(mNotificationRoundnessManager.shouldRoundNotificationPulsing()).thenReturn(true);
-        when(mTestHelper.getStatusBarStateController().isDozing()).thenReturn(true);
-
-        // Pulsing: Enabled
-        mRow.setHeadsUp(true);
-        mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(mEntry);
-
-        String debugString = mRow.getRoundableState().debugString();
-        assertEquals(
-                "If Pulsing is enabled, roundness should be set to 1. Value: " + debugString,
-                /* expected = */ 1,
-                /* actual = */ mRow.getTopRoundness(),
-                /* delta = */ 0.001
-        );
-        assertTrue(debugString.contains("Pulsing"));
-
-        // Pulsing: Disabled
-        mRow.setHeadsUp(false);
-        mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(mEntry);
-
-        assertEquals(
-                "If Pulsing is disabled, roundness should be set to 0. Value: "
-                        + mRow.getRoundableState().debugString(),
-                /* expected = */ 0,
-                /* actual = */ mRow.getTopRoundness(),
-                /* delta = */ 0.001
-        );
-    }
-
-    @Test
-    public void testPulsingRoundness_onHeadsUpStateChanged() {
-        // Pulsing: Enable flag and dozing
-        when(mNotificationRoundnessManager.shouldRoundNotificationPulsing()).thenReturn(true);
-        when(mTestHelper.getStatusBarStateController().isDozing()).thenReturn(true);
-
-        // Pulsing: Enabled
-        mEntry.setHeadsUp(true);
-        mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, true);
-
-        String debugString = mRow.getRoundableState().debugString();
-        assertEquals(
-                "If Pulsing is enabled, roundness should be set to 1. Value: " + debugString,
-                /* expected = */ 1,
-                /* actual = */ mRow.getTopRoundness(),
-                /* delta = */ 0.001
-        );
-        assertTrue(debugString.contains("Pulsing"));
-
-        // Pulsing: Disabled
-        mEntry.setHeadsUp(false);
-        mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, false);
-
-        assertEquals(
-                "If Pulsing is disabled, roundness should be set to 0. Value: "
-                        + mRow.getRoundableState().debugString(),
-                /* expected = */ 0,
-                /* actual = */ mRow.getTopRoundness(),
-                /* delta = */ 0.001
-        );
-    }
-
-    @Test
-    public void onHeadsUpStateChanged_true_transitionsNotified() {
-        mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, true);
-
-        verify(mPhoneStatusBarTransitions).onHeadsUpStateChanged(true);
-    }
-
-    @Test
-    public void onHeadsUpStateChanged_false_transitionsNotified() {
-        mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, false);
-
-        verify(mPhoneStatusBarTransitions).onHeadsUpStateChanged(false);
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
new file mode 100644
index 0000000..d017402
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2018 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.phone
+
+import android.platform.test.annotations.DisableFlags
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeHeadsUpTracker
+import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.HeadsUpStatusBarView
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.policy.Clock
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class HeadsUpAppearanceControllerTest : SysuiTestCase() {
+    private val mStackScrollerController: NotificationStackScrollLayoutController =
+        mock<NotificationStackScrollLayoutController>()
+    private val mShadeViewController: ShadeViewController = mock<ShadeViewController>()
+    private val mShadeHeadsUpTracker: ShadeHeadsUpTracker = mock<ShadeHeadsUpTracker>()
+    private val mDarkIconDispatcher: DarkIconDispatcher = mock<DarkIconDispatcher>()
+    private var mHeadsUpAppearanceController: HeadsUpAppearanceController? = null
+    private var mTestHelper: NotificationTestHelper? = null
+    private var mRow: ExpandableNotificationRow? = null
+    private var mEntry: NotificationEntry? = null
+    private var mHeadsUpStatusBarView: HeadsUpStatusBarView? = null
+    private var mHeadsUpManager: HeadsUpManager? = null
+    private var mOperatorNameView: View? = null
+    private var mStatusbarStateController: StatusBarStateController? = null
+    private var mPhoneStatusBarTransitions: PhoneStatusBarTransitions? = null
+    private var mBypassController: KeyguardBypassController? = null
+    private var mWakeUpCoordinator: NotificationWakeUpCoordinator? = null
+    private var mKeyguardStateController: KeyguardStateController? = null
+    private var mCommandQueue: CommandQueue? = null
+    private var mNotificationRoundnessManager: NotificationRoundnessManager? = null
+
+    @Before
+    @Throws(Exception::class)
+    fun setUp() {
+        allowTestableLooperAsMainThread()
+        mTestHelper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+        mRow = mTestHelper!!.createRow()
+        mEntry = mRow!!.entry
+        mHeadsUpStatusBarView = HeadsUpStatusBarView(mContext, mock<View>(), mock<TextView>())
+        mHeadsUpManager = mock<HeadsUpManager>()
+        mOperatorNameView = View(mContext)
+        mStatusbarStateController = mock<StatusBarStateController>()
+        mPhoneStatusBarTransitions = mock<PhoneStatusBarTransitions>()
+        mBypassController = mock<KeyguardBypassController>()
+        mWakeUpCoordinator = mock<NotificationWakeUpCoordinator>()
+        mKeyguardStateController = mock<KeyguardStateController>()
+        mCommandQueue = mock<CommandQueue>()
+        mNotificationRoundnessManager = mock<NotificationRoundnessManager>()
+        whenever(mShadeViewController.shadeHeadsUpTracker).thenReturn(mShadeHeadsUpTracker)
+        mHeadsUpAppearanceController =
+            HeadsUpAppearanceController(
+                mHeadsUpManager,
+                mStatusbarStateController,
+                mPhoneStatusBarTransitions,
+                mBypassController,
+                mWakeUpCoordinator,
+                mDarkIconDispatcher,
+                mKeyguardStateController,
+                mCommandQueue,
+                mStackScrollerController,
+                mShadeViewController,
+                mNotificationRoundnessManager,
+                mHeadsUpStatusBarView,
+                Clock(mContext, null),
+                mock<HeadsUpNotificationIconInteractor>(),
+                Optional.of(mOperatorNameView!!),
+            )
+        mHeadsUpAppearanceController!!.setAppearFraction(0.0f, 0.0f)
+    }
+
+    @Test
+    fun testShowinEntryUpdated() {
+        mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
+        whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
+        whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
+        mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
+        assertThat(mHeadsUpStatusBarView!!.showingEntry).isEqualTo(mRow!!.entry)
+
+        mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
+        whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
+        mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
+        assertThat(mHeadsUpStatusBarView!!.showingEntry).isNull()
+    }
+
+    @Test
+    fun testPinnedStatusUpdated() {
+        mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
+        whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
+        whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
+        mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
+        assertThat(mHeadsUpAppearanceController!!.pinnedStatus)
+            .isEqualTo(PinnedStatus.PinnedBySystem)
+
+        mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
+        whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
+        mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
+        assertThat(mHeadsUpAppearanceController!!.pinnedStatus).isEqualTo(PinnedStatus.NotPinned)
+    }
+
+    @Test
+    @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
+    fun testHeaderUpdated() {
+        mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
+        whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
+        whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
+        mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
+        assertThat(mRow!!.headerVisibleAmount).isEqualTo(0.0f)
+
+        mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
+        whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
+        mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
+        assertThat(mRow!!.headerVisibleAmount).isEqualTo(1.0f)
+    }
+
+    @Test
+    fun testOperatorNameViewUpdated() {
+        mHeadsUpAppearanceController!!.setAnimationsEnabled(false)
+
+        mRow!!.setPinnedStatus(PinnedStatus.PinnedBySystem)
+        whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(true)
+        whenever(mHeadsUpManager!!.getTopEntry()).thenReturn(mEntry)
+        mHeadsUpAppearanceController!!.onHeadsUpPinned(mEntry)
+        assertThat(mOperatorNameView!!.visibility).isEqualTo(View.INVISIBLE)
+
+        mRow!!.setPinnedStatus(PinnedStatus.NotPinned)
+        whenever(mHeadsUpManager!!.hasPinnedHeadsUp()).thenReturn(false)
+        mHeadsUpAppearanceController!!.onHeadsUpUnPinned(mEntry)
+        assertThat(mOperatorNameView!!.visibility).isEqualTo(View.VISIBLE)
+    }
+
+    @Test
+    fun constructor_animationValuesUpdated() {
+        val appearFraction = .75f
+        val expandedHeight = 400f
+        whenever(mStackScrollerController.appearFraction).thenReturn(appearFraction)
+        whenever(mStackScrollerController.expandedHeight).thenReturn(expandedHeight)
+
+        val newController =
+            HeadsUpAppearanceController(
+                mHeadsUpManager,
+                mStatusbarStateController,
+                mPhoneStatusBarTransitions,
+                mBypassController,
+                mWakeUpCoordinator,
+                mDarkIconDispatcher,
+                mKeyguardStateController,
+                mCommandQueue,
+                mStackScrollerController,
+                mShadeViewController,
+                mNotificationRoundnessManager,
+                mHeadsUpStatusBarView,
+                Clock(mContext, null),
+                mock<HeadsUpNotificationIconInteractor>(),
+                Optional.empty(),
+            )
+
+        assertThat(newController.mExpandedHeight).isEqualTo(expandedHeight)
+        assertThat(newController.mAppearFraction).isEqualTo(appearFraction)
+    }
+
+    @Test
+    fun testDestroy() {
+        reset(mHeadsUpManager)
+        reset(mDarkIconDispatcher)
+        reset(mShadeHeadsUpTracker)
+        reset(mStackScrollerController)
+
+        mHeadsUpAppearanceController!!.onViewDetached()
+
+        verify(mHeadsUpManager!!).removeListener(any())
+        verify(mDarkIconDispatcher).removeDarkReceiver(any())
+        verify(mShadeHeadsUpTracker).removeTrackingHeadsUpListener(any())
+        verify(mShadeHeadsUpTracker).setHeadsUpAppearanceController(null)
+        verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any())
+    }
+
+    @Test
+    fun testPulsingRoundness_onUpdateHeadsUpAndPulsingRoundness() {
+        // Pulsing: Enable flag and dozing
+        whenever(mNotificationRoundnessManager!!.shouldRoundNotificationPulsing()).thenReturn(true)
+        whenever(mTestHelper!!.statusBarStateController.isDozing).thenReturn(true)
+
+        // Pulsing: Enabled
+        mRow!!.isHeadsUp = true
+        mHeadsUpAppearanceController!!.updateHeadsUpAndPulsingRoundness(mEntry)
+
+        val debugString: String = mRow!!.roundableState.debugString()
+        // If Pulsing is enabled, roundness should be set to 1
+        assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(1.0)
+        assertThat(debugString).contains("Pulsing")
+
+        // Pulsing: Disabled
+        mRow!!.isHeadsUp = false
+        mHeadsUpAppearanceController!!.updateHeadsUpAndPulsingRoundness(mEntry)
+
+        // If Pulsing is disabled, roundness should be set to 0
+        assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(0.0)
+    }
+
+    @Test
+    fun testPulsingRoundness_onHeadsUpStateChanged() {
+        // Pulsing: Enable flag and dozing
+        whenever(mNotificationRoundnessManager!!.shouldRoundNotificationPulsing()).thenReturn(true)
+        whenever(mTestHelper!!.statusBarStateController.isDozing).thenReturn(true)
+
+        // Pulsing: Enabled
+        mEntry!!.setHeadsUp(true)
+        mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, true)
+
+        val debugString: String = mRow!!.roundableState.debugString()
+        // If Pulsing is enabled, roundness should be set to 1
+        assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(1.0)
+        assertThat(debugString).contains("Pulsing")
+
+        // Pulsing: Disabled
+        mEntry!!.setHeadsUp(false)
+        mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, false)
+
+        // If Pulsing is disabled, roundness should be set to 0
+        assertThat(mRow!!.topRoundness.toDouble()).isWithin(0.001).of(0.0)
+    }
+
+    @Test
+    fun onHeadsUpStateChanged_true_transitionsNotified() {
+        mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, true)
+
+        verify(mPhoneStatusBarTransitions!!).onHeadsUpStateChanged(true)
+    }
+
+    @Test
+    fun onHeadsUpStateChanged_false_transitionsNotified() {
+        mHeadsUpAppearanceController!!.onHeadsUpStateChanged(mEntry!!, false)
+
+        verify(mPhoneStatusBarTransitions!!).onHeadsUpStateChanged(false)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
index 110dec6..f76ee5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
@@ -87,7 +87,7 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @OptIn(ExperimentalCoroutinesApi::class)
-@DisableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP)
+@DisableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP, StatusBarChipsModernization.FLAG_NAME)
 class OngoingCallControllerViaListenerTest : SysuiTestCase() {
     private val kosmos = Kosmos()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
index 2ad50cc..647b5f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
@@ -29,6 +29,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
+import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
 import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
 import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
 import com.android.systemui.SysuiTestCase
@@ -77,6 +78,7 @@
 @TestableLooper.RunWithLooper
 @OptIn(ExperimentalCoroutinesApi::class)
 @EnableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP)
+@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
 class OngoingCallControllerViaRepoTest : SysuiTestCase() {
     private val kosmos = Kosmos()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index 4c6eaa5..a446313 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -16,10 +16,13 @@
 
 package com.android.systemui.statusbar.phone.ongoingcall.data.repository
 
+import android.platform.test.annotations.DisableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
 import com.google.common.truth.Truth.assertThat
@@ -28,6 +31,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
 class OngoingCallRepositoryTest : SysuiTestCase() {
     private val kosmos = Kosmos()
     private val underTest = kosmos.ongoingCallRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
new file mode 100644
index 0000000..ca1413e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.phone.ongoingcall.domain.interactor
+
+import android.app.PendingIntent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.CallType
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OngoingCallInteractorTest : SysuiTestCase() {
+    private val kosmos = Kosmos().useUnconfinedTestDispatcher()
+    private val repository = kosmos.activeNotificationListRepository
+    private val underTest = kosmos.ongoingCallInteractor
+
+    @Test
+    fun noNotification_emitsNoCall() = runTest {
+        val state by collectLastValue(underTest.ongoingCallState)
+        assertThat(state).isInstanceOf(OngoingCallModel.NoCall::class.java)
+    }
+
+    @Test
+    fun ongoingCallNotification_setsNotificationIconAndIntent() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.ongoingCallState)
+
+            // Set up notification with icon view and intent
+            val testIconView: StatusBarIconView = mock()
+            val testIntent: PendingIntent = mock()
+            repository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply {
+                        addIndividualNotif(
+                            activeNotificationModel(
+                                key = "notif1",
+                                whenTime = 1000L,
+                                callType = CallType.Ongoing,
+                                statusBarChipIcon = testIconView,
+                                contentIntent = testIntent
+                            )
+                        )
+                    }
+                    .build()
+
+            // Verify model is InCall and has the correct icon and intent.
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+            val model = latest as OngoingCallModel.InCall
+            assertThat(model.notificationIconView).isSameInstanceAs(testIconView)
+            assertThat(model.intent).isSameInstanceAs(testIntent)
+        }
+
+    @Test
+    fun ongoingCallNotification_emitsInCall() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.ongoingCallState)
+
+            repository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply {
+                        addIndividualNotif(
+                            activeNotificationModel(
+                                key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
+                            )
+                        )
+                    }
+                    .build()
+
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+        }
+
+    @Test
+    fun notificationRemoved_emitsNoCall() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.ongoingCallState)
+
+            repository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply {
+                        addIndividualNotif(
+                            activeNotificationModel(
+                                key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
+                            )
+                        )
+                    }
+                    .build()
+
+            repository.activeNotifications.value = ActiveNotificationsStore()
+            assertThat(latest).isInstanceOf(OngoingCallModel.NoCall::class.java)
+        }
+
+    @Test
+    fun ongoingCallNotification_appVisibleInitially_emitsInCallWithVisibleApp() =
+        kosmos.runTest {
+            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = true
+            val latest by collectLastValue(underTest.ongoingCallState)
+
+            repository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply {
+                        addIndividualNotif(
+                            activeNotificationModel(
+                                key = "notif1",
+                                whenTime = 1000L,
+                                callType = CallType.Ongoing,
+                                uid = UID
+                            )
+                        )
+                    }
+                    .build()
+
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+        }
+
+    @Test
+    fun ongoingCallNotification_appNotVisibleInitially_emitsInCall() =
+        kosmos.runTest {
+            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
+            val latest by collectLastValue(underTest.ongoingCallState)
+
+            repository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply {
+                        addIndividualNotif(
+                            activeNotificationModel(
+                                key = "notif1",
+                                whenTime = 1000L,
+                                callType = CallType.Ongoing,
+                                uid = UID
+                            )
+                        )
+                    }
+                    .build()
+
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+        }
+
+    @Test
+    fun ongoingCallNotification_visibilityChanges_updatesState() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.ongoingCallState)
+
+            // Start with notification and app not visible
+            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
+            repository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply {
+                        addIndividualNotif(
+                            activeNotificationModel(
+                                key = "notif1",
+                                whenTime = 1000L,
+                                callType = CallType.Ongoing,
+                                uid = UID
+                            )
+                        )
+                    }
+                    .build()
+            assertThat(latest)
+                .isInstanceOf(OngoingCallModel.InCall::class.java)
+
+            // App becomes visible
+            kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+
+            // App becomes invisible again
+            kosmos.activityManagerRepository.fake.setIsAppVisible(UID, false)
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+        }
+
+    companion object {
+        private const val UID = 885
+    }
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bc81a4b..e417da4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3814,6 +3814,9 @@
          shortcut helper The helper is a  component that shows the  user which keyboard shortcuts
          they can use. [CHAR LIMIT=NONE] -->
     <string name="shortcut_helper_customize_button_text">Customize</string>
+    <!-- Description text of the button that allows user to resets all custom shortcuts in keyboard
+         shortcut helper when in customization mode. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_reset_button_text">Reset</string>
     <!-- Description text of the button that allows user to exit shortcut customization mode in
          keyboard shortcut helper The helper is a  component that shows the  user which keyboard
          shortcuts they can use. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 071cf8a..5af80cb 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -63,6 +63,7 @@
 import com.android.systemui.plugins.clocks.ZenData
 import com.android.systemui.plugins.clocks.ZenData.ZenMode
 import com.android.systemui.res.R as SysuiR
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.regionsampling.RegionSampler
 import com.android.systemui.statusbar.policy.BatteryController
@@ -466,6 +467,15 @@
         batteryController.addCallback(batteryCallback)
         keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
         zenModeController.addCallback(zenModeCallback)
+        if (SceneContainerFlag.isEnabled) {
+            handleDoze(
+                when (AOD) {
+                    keyguardTransitionInteractor.getCurrentState() -> 1f
+                    keyguardTransitionInteractor.getStartedState() -> 1f
+                    else -> 0f
+                }
+            )
+        }
         disposableHandle =
             parent.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.CREATED) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
index 1401678..37433ca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.keyboard.shortcut.shared.model
 
-data class ShortcutSubCategory(val label: String, val shortcuts: List<Shortcut>)
+data class ShortcutSubCategory(val label: String, val shortcuts: List<Shortcut>) {
+    val containsCustomShortcuts: Boolean = shortcuts.any { it.containsCustomShortcutCommands }
+}
 
 class ShortcutSubCategoryBuilder(val label: String) {
     private val shortcuts = mutableListOf<Shortcut>()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
index bd0430b..274fa59 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
@@ -53,15 +53,18 @@
 
     override suspend fun onActivated(): Nothing {
         viewModel.shortcutCustomizationUiState.collect { uiState ->
-            val shouldShowAddDialog = uiState is AddShortcutDialog && !uiState.isDialogShowing
-            val shouldShowDeleteDialog = uiState is DeleteShortcutDialog && !uiState.isDialogShowing
-            val shouldShowResetDialog = uiState is ResetShortcutDialog && !uiState.isDialogShowing
-            if (shouldShowDeleteDialog || shouldShowAddDialog || shouldShowResetDialog) {
-                dialog = createDialog().also { it.show() }
-                viewModel.onDialogShown()
-            } else if (uiState is ShortcutCustomizationUiState.Inactive) {
-                dialog?.dismiss()
-                dialog = null
+            when(uiState){
+                is AddShortcutDialog,
+                is DeleteShortcutDialog,
+                is ResetShortcutDialog -> {
+                    if (dialog == null){
+                        dialog = createDialog().also { it.show() }
+                    }
+                }
+                is ShortcutCustomizationUiState.Inactive -> {
+                    dialog?.dismiss()
+                    dialog = null
+                }
             }
         }
         awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 7929307..2fdcf87 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -19,6 +19,7 @@
 import android.graphics.drawable.Icon
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
@@ -56,6 +57,7 @@
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.DeleteOutline
 import androidx.compose.material.icons.filled.ExpandMore
+import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material.icons.filled.Tune
 import androidx.compose.material3.CenterAlignedTopAppBar
@@ -113,7 +115,6 @@
 import androidx.compose.ui.util.fastForEachIndexed
 import com.android.compose.modifiers.thenIf
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
-import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
@@ -125,6 +126,7 @@
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
 import com.android.systemui.res.R
 import kotlinx.coroutines.delay
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
 
 @Composable
 fun ShortcutHelper(
@@ -187,6 +189,7 @@
             onKeyboardSettingsClicked,
             shortcutsUiState.isShortcutCustomizerFlagEnabled,
             onCustomizationRequested,
+            shortcutsUiState.shouldShowResetButton
         )
     }
 }
@@ -377,6 +380,7 @@
     onKeyboardSettingsClicked: () -> Unit,
     isShortcutCustomizerFlagEnabled: Boolean,
     onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
+    shouldShowResetButton: Boolean
 ) {
     val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
     var isCustomizing by remember { mutableStateOf(false) }
@@ -389,11 +393,14 @@
                     TitleBar(isCustomizing)
                 }
                 if (isShortcutCustomizerFlagEnabled) {
-                    if (isCustomizing) {
-                        DoneButton(onClick = { isCustomizing = false })
-                    } else {
-                        CustomizeButton(onClick = { isCustomizing = true })
-                    }
+                    CustomizationButtonsContainer(
+                        isCustomizing = isCustomizing,
+                        onToggleCustomizationMode = { isCustomizing = !isCustomizing },
+                        onReset = {
+                            onCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
+                        },
+                        shouldShowResetButton = shouldShowResetButton,
+                    )
                 } else {
                     Spacer(modifier = Modifier.width(if (isCustomizing) 69.dp else 133.dp))
                 }
@@ -422,6 +429,38 @@
 }
 
 @Composable
+private fun CustomizationButtonsContainer(
+    isCustomizing: Boolean,
+    shouldShowResetButton: Boolean,
+    onToggleCustomizationMode: () -> Unit,
+    onReset: () -> Unit,
+) {
+    Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+        if (isCustomizing) {
+            if (shouldShowResetButton) {
+                ResetButton(onClick = onReset)
+            }
+            DoneButton(onClick = onToggleCustomizationMode)
+        } else {
+            CustomizeButton(onClick = onToggleCustomizationMode)
+        }
+    }
+}
+
+@Composable
+private fun ResetButton(onClick: () -> Unit) {
+    ShortcutHelperButton(
+        onClick = onClick,
+        color = Color.Transparent,
+        width = 99.dp,
+        iconSource = IconSource(imageVector = Icons.Default.Refresh),
+        text = stringResource(id = R.string.shortcut_helper_reset_button_text),
+        contentColor = MaterialTheme.colorScheme.primary,
+        border = BorderStroke(color = MaterialTheme.colorScheme.outlineVariant, width = 1.dp),
+    )
+}
+
+@Composable
 private fun CustomizeButton(onClick: () -> Unit) {
     ShortcutHelperButton(
         onClick = onClick,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
index 58ce194..55c0fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
@@ -228,74 +228,8 @@
     contentColor: Color,
     contentPaddingHorizontal: Dp = 16.dp,
     contentPaddingVertical: Dp = 10.dp,
-) {
-    ClickableShortcutSurface(
-        onClick = onClick,
-        shape = shape,
-        color = color,
-        modifier = modifier.semantics { role = Role.Button }.width(width).height(height),
-        interactionsConfig =
-            InteractionsConfig(
-                hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
-                hoverOverlayAlpha = 0.11f,
-                pressedOverlayColor = MaterialTheme.colorScheme.onSurface,
-                pressedOverlayAlpha = 0.15f,
-                focusOutlineColor = MaterialTheme.colorScheme.secondary,
-                focusOutlineStrokeWidth = 3.dp,
-                focusOutlinePadding = 2.dp,
-                surfaceCornerRadius = 28.dp,
-                focusOutlineCornerRadius = 33.dp,
-            ),
-    ) {
-        Row(
-            modifier =
-                Modifier.padding(
-                    horizontal = contentPaddingHorizontal,
-                    vertical = contentPaddingVertical,
-                ),
-            verticalAlignment = Alignment.CenterVertically,
-            horizontalArrangement = Arrangement.Center,
-        ) {
-            if (iconSource.imageVector != null) {
-                Icon(
-                    tint = contentColor,
-                    imageVector = iconSource.imageVector,
-                    contentDescription = null,
-                    modifier = Modifier.size(20.dp).wrapContentSize(Alignment.Center),
-                )
-            }
-
-            if (iconSource.imageVector != null && text != null) {
-                Spacer(modifier = Modifier.weight(1f))
-            }
-
-            if (text != null) {
-                Text(
-                    text,
-                    color = contentColor,
-                    fontSize = 14.sp,
-                    style = MaterialTheme.typography.labelLarge,
-                    modifier = Modifier.wrapContentSize(Alignment.Center),
-                )
-            }
-        }
-    }
-}
-
-@Composable
-fun ShortcutHelperButton(
-    modifier: Modifier = Modifier,
-    onClick: () -> Unit,
-    shape: Shape = RoundedCornerShape(360.dp),
-    color: Color,
-    width: Dp,
-    height: Dp = 40.dp,
-    iconSource: IconSource = IconSource(),
-    text: String? = null,
-    contentColor: Color,
-    contentPaddingHorizontal: Dp = 16.dp,
-    contentPaddingVertical: Dp = 10.dp,
     enabled: Boolean = true,
+    border: BorderStroke? = null,
 ) {
     ShortcutHelperButtonSurface(
         onClick = onClick,
@@ -305,6 +239,7 @@
         enabled = enabled,
         width = width,
         height = height,
+        border = border,
     ) {
         Row(
             modifier =
@@ -342,7 +277,7 @@
 }
 
 @Composable
-fun ShortcutHelperButtonSurface(
+private fun ShortcutHelperButtonSurface(
     onClick: () -> Unit,
     shape: Shape,
     color: Color,
@@ -350,6 +285,7 @@
     enabled: Boolean,
     width: Dp,
     height: Dp,
+    border: BorderStroke?,
     content: @Composable () -> Unit,
 ) {
     if (enabled) {
@@ -357,6 +293,7 @@
             onClick = onClick,
             shape = shape,
             color = color,
+            border = border,
             modifier = modifier.semantics { role = Role.Button }.width(width).height(height),
             interactionsConfig =
                 InteractionsConfig(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt
index f5d478b..2728772 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt
@@ -31,4 +31,6 @@
         iconSource: IconSource,
         shortcutCategory: ShortcutCategory,
     ) : this(label, iconSource, shortcutCategory.type, shortcutCategory.subCategories)
+
+    val containsCustomShortcuts: Boolean = subCategories.any { it.containsCustomShortcuts }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
index bfc9486..36c5ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
@@ -23,17 +23,12 @@
         val shortcutLabel: String,
         val errorMessage: String = "",
         val defaultCustomShortcutModifierKey: ShortcutKey.Icon.ResIdIcon,
-        val isDialogShowing: Boolean = false,
         val pressedKeys: List<ShortcutKey> = emptyList(),
     ) : ShortcutCustomizationUiState
 
-    data class DeleteShortcutDialog(
-        val isDialogShowing: Boolean = false
-    ) : ShortcutCustomizationUiState
+    data object DeleteShortcutDialog : ShortcutCustomizationUiState
 
-    data class ResetShortcutDialog(
-        val isDialogShowing: Boolean = false
-    ) : ShortcutCustomizationUiState
+    data object ResetShortcutDialog : ShortcutCustomizationUiState
 
     data object Inactive : ShortcutCustomizationUiState
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
index 02b0b43..52ab157 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
@@ -25,6 +25,7 @@
         val shortcutCategories: List<ShortcutCategoryUi>,
         val defaultSelectedCategory: ShortcutCategoryType?,
         val isShortcutCustomizerFlagEnabled: Boolean = false,
+        val shouldShowResetButton: Boolean = false,
     ) : ShortcutsUiState
 
     data object Inactive : ShortcutsUiState
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
index 76a2e60..92e2592 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -73,32 +73,22 @@
                         shortcutLabel = requestInfo.label,
                         defaultCustomShortcutModifierKey =
                             shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey(),
-                        isDialogShowing = false,
                         pressedKeys = emptyList(),
                     )
                 shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
             }
 
             is ShortcutCustomizationRequestInfo.Delete -> {
-                _shortcutCustomizationUiState.value = DeleteShortcutDialog(isDialogShowing = false)
+                _shortcutCustomizationUiState.value = DeleteShortcutDialog
                 shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
             }
 
             ShortcutCustomizationRequestInfo.Reset -> {
-                _shortcutCustomizationUiState.value = ResetShortcutDialog(isDialogShowing = false)
+                _shortcutCustomizationUiState.value = ResetShortcutDialog
             }
         }
     }
 
-    fun onDialogShown() {
-        _shortcutCustomizationUiState.update { uiState ->
-            (uiState as? AddShortcutDialog)?.copy(isDialogShowing = true)
-                ?: (uiState as? DeleteShortcutDialog)?.copy(isDialogShowing = true)
-                ?: (uiState as? ResetShortcutDialog)?.copy(isDialogShowing = true)
-                ?: uiState
-        }
-    }
-
     fun onDialogDismissed() {
         _shortcutCustomizationUiState.value = ShortcutCustomizationUiState.Inactive
         shortcutCustomizationInteractor.onCustomizationRequested(null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 08fd0af8..0f5f073 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyboard.shortcut.ui.viewmodel
 
 import android.app.role.RoleManager
+import android.content.Context
 import android.content.pm.PackageManager.NameNotFoundException
 import android.util.Log
 import androidx.compose.material.icons.Icons
@@ -40,7 +41,6 @@
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -51,10 +51,12 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.withContext
+import javax.inject.Inject
 
 class ShortcutHelperViewModel
 @Inject
 constructor(
+    private val context: Context,
     private val roleManager: RoleManager,
     private val userTracker: UserTracker,
     @Background private val backgroundScope: CoroutineScope,
@@ -88,6 +90,7 @@
                         shortcutCategories = shortcutCategoriesUi,
                         defaultSelectedCategory = getDefaultSelectedCategory(filteredCategories),
                         isShortcutCustomizerFlagEnabled = keyboardShortcutHelperShortcutCustomizer(),
+                        shouldShowResetButton = shouldShowResetButton(shortcutCategoriesUi)
                     )
                 }
             }
@@ -97,6 +100,10 @@
                 initialValue = ShortcutsUiState.Inactive,
             )
 
+    private fun shouldShowResetButton(categoriesUi: List<ShortcutCategoryUi>): Boolean {
+        return categoriesUi.any { it.containsCustomShortcuts }
+    }
+
     private fun convertCategoriesModelToUiModel(
         categories: List<ShortcutCategory>
     ): List<ShortcutCategoryUi> {
@@ -136,13 +143,13 @@
     private fun getShortcutCategoryLabel(type: ShortcutCategoryType): String =
         when (type) {
             ShortcutCategoryType.System ->
-                userContext.getString(R.string.shortcut_helper_category_system)
+                context.getString(R.string.shortcut_helper_category_system)
             ShortcutCategoryType.MultiTasking ->
-                userContext.getString(R.string.shortcut_helper_category_multitasking)
+                context.getString(R.string.shortcut_helper_category_multitasking)
             ShortcutCategoryType.InputMethodEditor ->
-                userContext.getString(R.string.shortcut_helper_category_input)
+                context.getString(R.string.shortcut_helper_category_input)
             ShortcutCategoryType.AppCategories ->
-                userContext.getString(R.string.shortcut_helper_category_app_shortcuts)
+                context.getString(R.string.shortcut_helper_category_app_shortcuts)
             is CurrentApp -> getApplicationLabelForCurrentApp(type)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
index 1def7b3..636f703 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
@@ -19,7 +19,6 @@
 import android.content.res.Resources
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.util.kotlin.emitOnStart
@@ -30,7 +29,7 @@
 class QuickQuickSettingsRowRepository
 @Inject
 constructor(
-    @Main private val resources: Resources,
+    @ShadeDisplayAware private val resources: Resources,
     @ShadeDisplayAware configurationRepository: ConfigurationRepository,
 ) {
     val rows =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 75debb6..9efdd98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -9,6 +9,7 @@
 import android.service.quicksettings.Tile
 import android.text.TextUtils
 import android.text.format.DateFormat
+import android.widget.Button
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.MetricsLogger
@@ -70,7 +71,10 @@
     }
 
     override fun newTileState(): QSTile.State {
-        return QSTile.State().apply { handlesLongClick = false }
+        return QSTile.State().apply {
+            handlesLongClick = false
+            expandedAccessibilityClassName = Button::class.java.name
+        }
     }
 
     override fun handleClick(expandable: Expandable?) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 404ace1..7213f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -21,6 +21,7 @@
 import android.os.Handler
 import android.os.Looper
 import android.service.quicksettings.Tile
+import android.widget.Button
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.animation.Expandable
@@ -163,6 +164,7 @@
         } else {
             state.state = Tile.STATE_UNAVAILABLE
         }
+        state.expandedAccessibilityClassName = Button::class.java.name
     }
 
     override fun getMetricsCategory(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
index 374bcda..e37ed16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -32,6 +32,7 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
+import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -176,6 +177,7 @@
         } else {
             state.state = isDreaming() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         }
+        state.expandedAccessibilityClassName = Switch.class.getName();
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index 48b39dc..74563ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -24,6 +24,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
+import android.widget.Button;
 
 import androidx.annotation.Nullable;
 
@@ -124,6 +125,7 @@
             state.state = Tile.STATE_INACTIVE;
             state.secondaryLabel = "";
         }
+        state.expandedAccessibilityClassName = Button.class.getName();
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index e9c5f4a..0a59529 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -30,7 +30,7 @@
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
-import android.widget.Switch;
+import android.widget.Button;
 
 import androidx.annotation.Nullable;
 
@@ -136,7 +136,7 @@
     }
 
     @Override
-    public void secondaryClick(@Nullable Expandable expandable) {
+    public void handleSecondaryClick(@Nullable Expandable expandable) {
         // TODO(b/358352265): Figure out the correct action for the secondary click
         // Toggle Wifi
         mWifiStateWorker.setWifiEnabled(!mWifiStateWorker.isWifiEnabled());
@@ -577,7 +577,7 @@
         state.contentDescription = minimalContentDescription.toString();
         state.dualLabelContentDescription = r.getString(
                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
-        state.expandedAccessibilityClassName = Switch.class.getName();
+        state.expandedAccessibilityClassName = Button.class.getName();
         if (DEBUG) {
             Log.d(TAG, "handleUpdateWifiState: " + "BooleanState = " + state.toString());
         }
@@ -594,7 +594,7 @@
         boolean mobileDataEnabled = mDataController.isMobileDataSupported()
                 && mDataController.isMobileDataEnabled();
         state.value = mobileDataEnabled;
-        state.expandedAccessibilityClassName = Switch.class.getName();
+        state.expandedAccessibilityClassName = Button.class.getName();
 
         if (cb.mAirplaneModeEnabled && cb.mQsTypeIcon != TelephonyIcons.ICON_CWF) {
             state.state = Tile.STATE_INACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 7225800..6d3e5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -20,7 +20,7 @@
 import android.os.Handler
 import android.os.Looper
 import android.provider.Settings
-import android.widget.Switch
+import android.widget.Button
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
@@ -71,7 +71,7 @@
         metricsLogger,
         statusBarStateController,
         activityStarter,
-        qsLogger
+        qsLogger,
     ) {
     private var model: InternetTileModel = viewModel.tileModel.value
 
@@ -110,7 +110,7 @@
         return InternetDetailsViewModel { longClick(null) }
     }
 
-    override fun secondaryClick(expandable: Expandable?) {
+    override fun handleSecondaryClick(expandable: Expandable?) {
         // TODO(b/358352265): Figure out the correct action for the secondary click
         // Toggle wifi
         wifiStateWorker.isWifiEnabled = !wifiStateWorker.isWifiEnabled
@@ -118,7 +118,7 @@
 
     override fun handleUpdateState(state: QSTile.BooleanState, arg: Any?) {
         state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
-        state.expandedAccessibilityClassName = Switch::class.java.name
+        state.expandedAccessibilityClassName = Button::class.java.name
 
         model.applyTo(state, mContext)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 93a51cf..467233d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -21,6 +21,7 @@
 import android.os.Looper;
 import android.service.quicksettings.Tile;
 import android.util.Log;
+import android.widget.Button;
 
 import androidx.annotation.Nullable;
 
@@ -126,6 +127,7 @@
         // would go to "Unavailable" state only when GMS core is updating.
         state.secondaryLabel = state.state == Tile.STATE_UNAVAILABLE
                 ? mContext.getString(R.string.qr_code_scanner_updating_secondary_label) : null;
+        state.expandedAccessibilityClassName = Button.class.getName();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 04e72d5..6deb192 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -40,6 +40,7 @@
 import android.service.quickaccesswallet.WalletCard;
 import android.service.quicksettings.Tile;
 import android.util.Log;
+import android.widget.Button;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -186,6 +187,7 @@
             state.secondaryLabel = null;
             state.sideViewCustomDrawable = null;
         }
+        state.expandedAccessibilityClassName = Button.class.getName();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 87f542e..aeb6cef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.base.viewmodel
 
 import android.os.UserHandle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Dumpable
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
@@ -58,11 +59,8 @@
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.transformLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
-import kotlinx.coroutines.withContext
 
 /**
  * Provides a hassle-free way to implement new tiles according to current System UI architecture
@@ -90,36 +88,35 @@
 
     private val users: MutableStateFlow<UserHandle> =
         MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
+
     private val userInputs: MutableSharedFlow<QSTileUserAction> = MutableSharedFlow()
+
     private val forceUpdates: MutableSharedFlow<Unit> = MutableSharedFlow()
     private val spec
         get() = config.tileSpec
 
-    private val tileData: SharedFlow<DATA_TYPE> = createTileDataFlow()
+    private val tileData: SharedFlow<DATA_TYPE?> = createTileDataFlow()
 
     override val state: StateFlow<QSTileState?> =
         tileData
             .map { data ->
-                withContext(uiBackgroundDispatcher) { mapper().map(config, data) }
-                    .also { state -> qsTileLogger.logStateUpdate(spec, state, data) }
+                data?.let {
+                    mapper().map(config, it).also { state ->
+                        qsTileLogger.logStateUpdate(spec, state, it)
+                    }
+                }
             }
-            .stateIn(
-                tileScope,
-                SharingStarted.WhileSubscribed(),
-                null,
-            )
+            .flowOn(uiBackgroundDispatcher)
+            .stateIn(tileScope, SharingStarted.WhileSubscribed(), null)
+
     override val isAvailable: StateFlow<Boolean> =
         users
             .flatMapLatest { tileDataInteractor().availability(it) }
             .flowOn(backgroundDispatcher)
-            .stateIn(
-                tileScope,
-                SharingStarted.WhileSubscribed(),
-                true,
-            )
+            .stateIn(tileScope, SharingStarted.WhileSubscribed(), true)
 
     override fun forceUpdate() {
-        tileScope.launch { forceUpdates.emit(Unit) }
+        tileScope.launch(context = backgroundDispatcher) { forceUpdates.emit(Unit) }
     }
 
     override fun onUserChanged(user: UserHandle) {
@@ -131,9 +128,9 @@
             userAction,
             spec,
             tileData.replayCache.isNotEmpty(),
-            state.replayCache.isNotEmpty()
+            state.replayCache.isNotEmpty(),
         )
-        tileScope.launch { userInputs.emit(userAction) }
+        tileScope.launch(context = backgroundDispatcher) { userInputs.emit(userAction) }
     }
 
     override fun destroy() {
@@ -147,7 +144,7 @@
             println(state.replayCache.lastOrNull().toString())
         }
 
-    private fun createTileDataFlow(): SharedFlow<DATA_TYPE> =
+    private fun createTileDataFlow(): SharedFlow<DATA_TYPE?> =
         users
             .transformLatest { user ->
                 coroutineScope {
@@ -159,6 +156,7 @@
                                     .onEach { qsTileLogger.logForceUpdate(spec) },
                             )
                             .onStart { qsTileLogger.logInitialRequest(spec) }
+                            .flowOn(backgroundDispatcher)
                             .stateIn(this, SharingStarted.Eagerly, DataUpdateTrigger.InitialRequest)
                     tileDataInteractor()
                         .tileData(user, updateTriggers)
@@ -171,11 +169,8 @@
                 }
             }
             .distinctUntilChanged()
-            .shareIn(
-                tileScope,
-                SharingStarted.WhileSubscribed(),
-                replay = 1, // we only care about the most recent value
-            )
+            .flowOn(backgroundDispatcher)
+            .stateIn(tileScope, SharingStarted.WhileSubscribed(), null)
 
     /**
      * Creates a user input flow which:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
index bb0467f..2eae3eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
@@ -23,6 +23,8 @@
 import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
 import com.android.systemui.statusbar.chips.StatusBarChipsLog
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.OngoingCallInteractor
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -37,17 +39,30 @@
 @Inject
 constructor(
     @Application private val scope: CoroutineScope,
+    ongoingCallInteractor: OngoingCallInteractor,
     repository: OngoingCallRepository,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) {
     val ongoingCallState: StateFlow<OngoingCallModel> =
-        repository.ongoingCallState
+        (if (StatusBarChipsModernization.isEnabled)
+            ongoingCallInteractor.ongoingCallState
+        else
+            repository.ongoingCallState)
             .onEach {
-                logger.log(TAG, LogLevel.INFO, { str1 = it::class.simpleName }, { "State: $str1" })
+                logger.log(
+                    TAG,
+                    LogLevel.INFO,
+                    { str1 = it::class.simpleName },
+                    { "State: $str1" }
+                )
             }
-            .stateIn(scope, SharingStarted.Lazily, OngoingCallModel.NoCall)
+            .stateIn(
+                scope,
+                SharingStarted.Lazily,
+                OngoingCallModel.NoCall
+            )
 
     companion object {
         private val TAG = "OngoingCall".pad()
     }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index b8cdd25..6f491e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -59,7 +59,8 @@
         interactor.ongoingCallState
             .map { state ->
                 when (state) {
-                    is OngoingCallModel.NoCall -> OngoingActivityChipModel.Hidden()
+                    is OngoingCallModel.NoCall,
+                    is OngoingCallModel.InCallWithVisibleApp -> OngoingActivityChipModel.Hidden()
                     is OngoingCallModel.InCall -> {
                         val icon =
                             if (
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
index 087b510..c57c807 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -105,7 +105,7 @@
             }
             return null
         }
-        return NotificationChipModel(key, statusBarChipIconView)
+        return NotificationChipModel(key, statusBarChipIconView, whenTime)
     }
 
     @AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
index 5698ee6..bc4241d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
@@ -19,4 +19,8 @@
 import com.android.systemui.statusbar.StatusBarIconView
 
 /** Modeling all the data needed to render a status bar notification chip. */
-data class NotificationChipModel(val key: String, val statusBarChipIconView: StatusBarIconView)
+data class NotificationChipModel(
+    val key: String,
+    val statusBarChipIconView: StatusBarIconView,
+    val whenTime: Long,
+)
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 9eff627..b2f7e2f 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
@@ -63,9 +63,13 @@
                     )
                 }
             }
-        return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+        return OngoingActivityChipModel.Shown.ShortTimeDelta(
+            icon,
+            colors,
+            time = this.whenTime,
+            onClickListener,
+        )
         // TODO(b/364653005): Use Notification.showWhen to determine if we should show the time.
-        // TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text.
         // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`.
         // TODO(b/364653005): If the app that posted the notification is in the foreground, don't
         // show that app's chip.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index f4462a4..730784a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -192,10 +192,14 @@
             }
             is OngoingActivityChipModel.Shown.ShortTimeDelta -> {
                 chipShortTimeDeltaView.setTime(chipModel.time)
-                // TODO(b/364653005): DateTimeView's relative time doesn't quite match the format we
-                //  want in the status bar chips.
-                chipShortTimeDeltaView.isShowRelativeTime = true
                 chipShortTimeDeltaView.visibility = View.VISIBLE
+                chipShortTimeDeltaView.isShowRelativeTime = true
+                chipShortTimeDeltaView.setRelativeTimeDisambiguationTextMask(
+                    DateTimeView.DISAMBIGUATION_TEXT_PAST
+                )
+                chipShortTimeDeltaView.setRelativeTimeUnitDisplayLength(
+                    DateTimeView.UNIT_DISPLAY_LENGTH_MEDIUM
+                )
 
                 chipTextView.visibility = View.GONE
                 chipTimeView.hide()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 47b695e..2588c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
 import com.android.systemui.statusbar.window.MultiDisplayStatusBarWindowControllerStore
@@ -63,11 +64,6 @@
 
     @Binds
     @IntoMap
-    @ClassKey(OngoingCallController::class)
-    fun bindOngoingCallController(impl: OngoingCallController): CoreStartable
-
-    @Binds
-    @IntoMap
     @ClassKey(LightBarController::class)
     fun lightBarControllerAsCoreStartable(controller: LightBarController): CoreStartable
 
@@ -90,6 +86,18 @@
     ): LightBarController.Factory
 
     companion object {
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(OngoingCallController::class)
+        fun ongoingCallController(
+            controller: OngoingCallController
+        ): CoreStartable =
+            if (StatusBarChipsModernization.isEnabled) {
+                CoreStartable.NOP
+            } else {
+                controller
+            }
 
         @Provides
         @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 6de4928..e4768e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -71,6 +71,7 @@
 import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent.Startable;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization;
 import com.android.systemui.statusbar.phone.ui.DarkIconManager;
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder;
@@ -960,7 +961,9 @@
             mOngoingCallController.addCallback(mOngoingCallListener);
         }
         // TODO(b/364653005): Do we also need to set the secondary activity chip?
-        mOngoingCallController.setChipView(mPrimaryOngoingActivityChip);
+        if (!StatusBarChipsModernization.isEnabled()) {
+            mOngoingCallController.setChipView(mPrimaryOngoingActivityChip);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 78926c7..2166304 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -60,7 +60,10 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 
-/** A controller to handle the ongoing call chip in the collapsed status bar. */
+/** A controller to handle the ongoing call chip in the collapsed status bar.
+ * @deprecated Use [OngoingCallInteractor] instead, which follows recommended architecture patterns
+ */
+@Deprecated("Use OngoingCallInteractor instead")
 @SysUISingleton
 class OngoingCallController
 @Inject
@@ -165,6 +168,9 @@
         }
 
     override fun start() {
+        if (StatusBarChipsModernization.isEnabled)
+            return
+
         dumpManager.registerDumpable(this)
 
         if (Flags.statusBarUseReposForCallChip()) {
@@ -201,6 +207,8 @@
      * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment].
      */
     fun setChipView(chipView: View) {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         tearDownChipView()
         this.chipView = chipView
         val backgroundView: ChipBackgroundContainer? =
@@ -217,6 +225,8 @@
      * Returns true if there's an active ongoing call that should be displayed in a status bar chip.
      */
     fun hasOngoingCall(): Boolean {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         return callNotificationInfo?.isOngoing == true &&
             // When the user is in the phone app, don't show the chip.
             !uidObserver.isCallAppVisible
@@ -224,6 +234,8 @@
 
     /** Creates the right [OngoingCallModel] based on the call state. */
     private fun getOngoingCallModel(): OngoingCallModel {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         if (hasOngoingCall()) {
             val currentInfo =
                 callNotificationInfo
@@ -255,6 +267,8 @@
     }
 
     override fun addCallback(listener: OngoingCallListener) {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         synchronized(mListeners) {
             if (!mListeners.contains(listener)) {
                 mListeners.add(listener)
@@ -263,10 +277,14 @@
     }
 
     override fun removeCallback(listener: OngoingCallListener) {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         synchronized(mListeners) { mListeners.remove(listener) }
     }
 
     private fun updateInfoFromNotifModel(notifModel: ActiveNotificationModel?) {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         if (notifModel == null) {
             logger.log(TAG, LogLevel.DEBUG, {}, { "NotifInteractorCallModel: null" })
             removeChip()
@@ -310,6 +328,8 @@
     }
 
     private fun updateChip() {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         val currentCallNotificationInfo = callNotificationInfo ?: return
 
         val currentChipView = chipView
@@ -360,6 +380,8 @@
     }
 
     private fun updateChipClickListener() {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         if (Flags.statusBarScreenSharingChips()) {
             return
         }
@@ -386,10 +408,14 @@
 
     /** Returns true if the given [procState] represents a process that's visible to the user. */
     private fun isProcessVisibleToUser(procState: Int): Boolean {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         return procState <= ActivityManager.PROCESS_STATE_TOP
     }
 
     private fun updateGestureListening() {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         if (
             callNotificationInfo == null ||
                 callNotificationInfo?.statusBarSwipedAway == true ||
@@ -404,6 +430,8 @@
     }
 
     private fun removeChip() {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         callNotificationInfo = null
         if (!Flags.statusBarScreenSharingChips()) {
             tearDownChipView()
@@ -432,6 +460,8 @@
      * detected.
      */
     private fun onSwipeAwayGestureDetected() {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         logger.log(TAG, LogLevel.DEBUG, {}, { "Swipe away gesture detected" })
         callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
         statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
@@ -441,6 +471,8 @@
     }
 
     private fun sendStateChangeEvent() {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         ongoingCallRepository.setOngoingCallState(getOngoingCallModel())
         mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
new file mode 100644
index 0000000..2ab2b68
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarChipsModernization.kt
@@ -0,0 +1,52 @@
+/*
+* 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.phone.ongoingcall
+
+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_use_interactor_for_call_chip flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarChipsModernization {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.statusBarChipsModernization() && Flags.statusBarRootModernization()
+
+    /**
+     * 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 enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index f16371a..b932c71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -33,7 +34,9 @@
  * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController] and
  * [com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore]. Instead, those two
  * classes both refer to this repository.
+ * @deprecated Use [OngoingCallInteractor] instead.
  */
+@Deprecated("Use OngoingCallInteractor instead")
 @SysUISingleton
 class OngoingCallRepository
 @Inject
@@ -49,6 +52,8 @@
      * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController].
      */
     fun setOngoingCallState(state: OngoingCallModel) {
+        StatusBarChipsModernization.assertInLegacyMode()
+
         logger.log(
             TAG,
             LogLevel.DEBUG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
new file mode 100644
index 0000000..1f7bd14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.phone.ongoingcall.domain.interactor
+
+import com.android.systemui.activity.data.repository.ActivityManagerRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Interactor for determining whether to show a chip in the status bar for ongoing phone calls.
+ *
+ * This class monitors call notifications and the visibility of call apps to determine the appropriate
+ * chip state. It emits:
+ *  * - [OngoingCallModel.NoCall] when there is no call notification
+ *  * - [OngoingCallModel.InCallWithVisibleApp] when there is a call notification but the call app is visible
+ *  * - [OngoingCallModel.InCall] when there is a call notification and the call app is not visible
+ *  */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class OngoingCallInteractor @Inject constructor(
+    @Application private val scope: CoroutineScope,
+    activityManagerRepository: ActivityManagerRepository,
+    activeNotificationsInteractor: ActiveNotificationsInteractor,
+    @OngoingCallLog private val logBuffer: LogBuffer,
+) {
+    private val logger = Logger(logBuffer, TAG)
+
+    /**
+     * The current state of ongoing calls.
+     */
+    val ongoingCallState: StateFlow<OngoingCallModel> =
+        activeNotificationsInteractor.ongoingCallNotification
+            .flatMapLatest { notificationModel ->
+                when (notificationModel) {
+                    null -> {
+                        logger.d("No active call notification - hiding chip")
+                        flowOf(OngoingCallModel.NoCall)
+                    }
+
+                    else -> combine(
+                        flowOf(notificationModel),
+                        activityManagerRepository.createIsAppVisibleFlow(
+                            creationUid = notificationModel.uid,
+                            logger = logger,
+                            identifyingLogTag = TAG,
+                        ),
+                    ) { model, isVisible ->
+                        when {
+                            isVisible -> {
+                                logger.d({ "Call app is visible: uid=$int1" }) {
+                                    int1 = model.uid
+                                }
+                                OngoingCallModel.InCallWithVisibleApp
+                            }
+
+                            else -> {
+                                logger.d({ "Active call detected: startTime=$long1 hasIcon=$bool1" }) {
+                                    long1 = model.whenTime
+                                    bool1 = model.statusBarChipIconView != null
+                                }
+                                OngoingCallModel.InCall(
+                                    startTimeMs = model.whenTime,
+                                    notificationIconView = model.statusBarChipIconView,
+                                    intent = model.contentIntent,
+                                )
+                            }
+                        }
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingCallModel.NoCall)
+
+    companion object {
+        private val TAG = "OngoingCall"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index 34bff80..c2c91b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -25,6 +25,12 @@
     data object NoCall : OngoingCallModel
 
     /**
+     * There is an ongoing call but the call app is currently visible, so we don't need to show
+     * the chip.
+     */
+    data object InCallWithVisibleApp : OngoingCallModel
+
+    /**
      * There *is* an ongoing call.
      *
      * @property startTimeMs the time that the phone call started, based on the notification's
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index 1e8b016..812e0eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.phone.StatusIconContainer
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ui.DarkIconManager
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder
@@ -150,12 +151,11 @@
                         )
                     iconController.addIconGroup(darkIconManager)
 
-                    // TODO(b/372657935): This won't be needed once OngoingCallController is
-                    // implemented in recommended architecture
-                    ongoingCallController.setChipView(
-                        phoneStatusBarView.requireViewById(R.id.ongoing_activity_chip_primary)
-                    )
-
+                    if (!StatusBarChipsModernization.isEnabled) {
+                        ongoingCallController.setChipView(
+                            phoneStatusBarView.requireViewById(R.id.ongoing_activity_chip_primary)
+                        )
+                    }
                     // For notifications, first inflate the [NotificationIconContainer]
                     val notificationIconArea =
                         phoneStatusBarView.requireViewById<ViewGroup>(R.id.notification_icon_area)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index d1e4f64..3e24fbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -68,6 +68,7 @@
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
 import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization;
 import com.android.systemui.statusbar.phone.ui.DarkIconManager;
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewBinder;
@@ -155,9 +156,9 @@
                 any(StatusBarWindowStateListener.class));
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testDisableNone() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testDisableNone() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
@@ -166,9 +167,9 @@
         assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -184,9 +185,9 @@
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
         // GIVEN the status bar hides the system info via disable flags, while there is no event
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -214,9 +215,9 @@
         assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
         // GIVEN the status bar hides the system info via disable flags, while there is no event
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -231,9 +232,9 @@
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
         // GIVEN the status bar is not disabled
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
@@ -247,9 +248,9 @@
         assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
         // GIVEN the status bar is not disabled
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
@@ -271,9 +272,9 @@
         assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testDisableNotifications() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testDisableNotifications() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
@@ -307,9 +308,9 @@
         assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testDisableClock() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testDisableClock() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
@@ -343,10 +344,10 @@
         assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_shadeOpenAndShouldHide_everythingHidden() {
+  @Test
+  @DisableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_shadeOpenAndShouldHide_everythingHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN the shade is open and configured to hide the status bar icons
@@ -361,10 +362,10 @@
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_shadeOpenButNotShouldHide_everythingShown() {
+  @Test
+  @DisableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_shadeOpenButNotShouldHide_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN the shade is open but *not* configured to hide the status bar icons
@@ -379,11 +380,11 @@
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
-    /** Regression test for b/279790651. */
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() {
+  /** Regression test for b/279790651. */
+  @Test
+  @DisableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN the shade is open and configured to hide the status bar icons
@@ -409,9 +410,9 @@
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_notTransitioningToOccluded_everythingShown() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_notTransitioningToOccluded_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().setValue(false);
@@ -424,10 +425,10 @@
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_isTransitioningToOccluded_everythingHidden() {
+  @Test
+  @DisableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_isTransitioningToOccluded_everythingHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().setValue(true);
@@ -440,10 +441,10 @@
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() {
+  @Test
+  @DisableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN the transition is occurring
@@ -472,9 +473,13 @@
         assertEquals(View.GONE, getUserChipView().getVisibility());
     }
 
-    @Test
-    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
-    public void disable_noOngoingCall_chipHidden() {
+  @Test
+  @DisableFlags({
+    FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void disable_noOngoingCall_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
@@ -484,9 +489,13 @@
         assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
-    public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
+  @Test
+  @DisableFlags({
+    FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -497,9 +506,13 @@
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
-    @Test
-    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
-    public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
+  @Test
+  @DisableFlags({
+    FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -510,9 +523,13 @@
         assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
-    public void disable_hasOngoingCallButAlsoHun_chipHidden() {
+  @Test
+  @DisableFlags({
+    FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void disable_hasOngoingCallButAlsoHun_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -523,9 +540,13 @@
         assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
-    public void disable_ongoingCallEnded_chipHidden() {
+  @Test
+  @DisableFlags({
+    FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void disable_ongoingCallEnded_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // Ongoing call started
@@ -547,9 +568,13 @@
         assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
-    public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
+  @Test
+  @DisableFlags({
+    FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
         fragment.enableAnimationsForTesting();
@@ -564,9 +589,13 @@
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
-    @Test
-    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
-    public void screenSharingChipsDisabled_ignoresNewCallback() {
+  @Test
+  @DisableFlags({
+    FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void screenSharingChipsDisabled_ignoresNewCallback() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN there *is* an ongoing call via old callback
@@ -597,10 +626,10 @@
         assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void noOngoingActivity_chipHidden() {
+  @Test
+  @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void noOngoingActivity_chipHidden() {
         resumeAndGetFragment();
 
         // TODO(b/332662551): We *should* be able to just set a value on
@@ -615,10 +644,10 @@
         assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() {
+  @Test
+  @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() {
         resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -630,12 +659,14 @@
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
-    @Test
-    @EnableFlags({
-            FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
-            StatusBarNotifChips.FLAG_NAME,
-            StatusBarRootModernization.FLAG_NAME})
-    public void hasPrimaryOngoingActivity_viewsUnchangedWhenRootModernizationFlagOn() {
+  @Test
+  @EnableFlags({
+    FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+    StatusBarNotifChips.FLAG_NAME,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void hasPrimaryOngoingActivity_viewsUnchangedWhenRootModernizationFlagOn() {
         resumeAndGetFragment();
 
         assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
@@ -658,10 +689,14 @@
         assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
     }
 
-    @Test
-    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
-    public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
+  @Test
+  @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+  @DisableFlags({
+    StatusBarNotifChips.FLAG_NAME,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
         resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -672,10 +707,10 @@
         assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
+  @Test
+  @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
         resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -687,10 +722,14 @@
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
-    @Test
-    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
-    public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
+  @Test
+  @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+  @DisableFlags({
+    StatusBarNotifChips.FLAG_NAME,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -704,10 +743,10 @@
         assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
+  @Test
+  @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -722,10 +761,14 @@
         assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
-    public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
+  @Test
+  @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+  @DisableFlags({
+    StatusBarNotifChips.FLAG_NAME,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -739,10 +782,10 @@
         assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
+  @Test
+  @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -757,10 +800,14 @@
         assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
-    public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
+  @Test
+  @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+  @DisableFlags({
+    StatusBarNotifChips.FLAG_NAME,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
         resumeAndGetFragment();
 
         // Ongoing activity started
@@ -780,10 +827,10 @@
         assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
+  @Test
+  @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
         resumeAndGetFragment();
 
         // Ongoing activity started
@@ -803,10 +850,10 @@
         assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void secondaryOngoingActivityEnded_chipHidden() {
+  @Test
+  @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void secondaryOngoingActivityEnded_chipHidden() {
         resumeAndGetFragment();
 
         // Secondary ongoing activity started
@@ -826,10 +873,14 @@
         assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
-    public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
+  @Test
+  @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+  @DisableFlags({
+    StatusBarNotifChips.FLAG_NAME,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
         fragment.enableAnimationsForTesting();
@@ -845,10 +896,10 @@
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
-    @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
+  @Test
+  @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
         fragment.enableAnimationsForTesting();
@@ -864,10 +915,14 @@
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
-    @Test
-    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
-    public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
+  @Test
+  @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+  @DisableFlags({
+    StatusBarNotifChips.FLAG_NAME,
+    StatusBarRootModernization.FLAG_NAME,
+    StatusBarChipsModernization.FLAG_NAME
+  })
+  public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN there *is* an ongoing call via old callback
@@ -897,10 +952,10 @@
         assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
+  @Test
+  @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN there *is* an ongoing call via old callback
@@ -931,10 +986,10 @@
         assertEquals(View.VISIBLE, getSecondaryOngoingActivityChipView().getVisibility());
     }
 
-    @Test
-    @EnableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void isHomeStatusBarAllowedByScene_false_everythingHidden() {
+  @Test
+  @EnableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void isHomeStatusBarAllowedByScene_false_everythingHidden() {
         resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(false);
@@ -945,10 +1000,10 @@
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @EnableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void isHomeStatusBarAllowedByScene_true_everythingShown() {
+  @Test
+  @EnableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void isHomeStatusBarAllowedByScene_true_everythingShown() {
         resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onIsHomeStatusBarAllowedBySceneChanged(true);
@@ -959,10 +1014,10 @@
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @EnableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() {
+  @Test
+  @EnableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN the scene doesn't allow the status bar
@@ -977,10 +1032,10 @@
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @EnableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() {
+  @Test
+  @EnableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN the scene does allow the status bar
@@ -995,10 +1050,10 @@
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() {
+  @Test
+  @DisableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() {
         resumeAndGetFragment();
 
         // Even if the scene says to hide the home status bar
@@ -1010,9 +1065,9 @@
         assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_isDozing_clockAndSystemInfoVisible() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_isDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(true);
 
@@ -1022,9 +1077,9 @@
         assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_NotDozing_clockAndSystemInfoVisible() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_NotDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(false);
 
@@ -1034,9 +1089,9 @@
         assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
 
@@ -1045,9 +1100,9 @@
         assertEquals(View.GONE, getClockView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
 
@@ -1098,10 +1153,10 @@
         assertFalse(contains);
     }
 
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
+  @Test
+  @DisableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
         final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mockSecureCameraLaunch(fragment, true /* launched */);
@@ -1121,10 +1176,10 @@
         assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() {
+  @Test
+  @DisableSceneContainer
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() {
         final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN a transition to dream has started
@@ -1158,9 +1213,9 @@
         assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
-    @Test
-    @DisableFlags(StatusBarRootModernization.FLAG_NAME)
-    public void testStatusBarIcons_lockscreenToDreamTransitionButNotDreaming_iconsVisible() {
+  @Test
+  @DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
+  public void testStatusBarIcons_lockscreenToDreamTransitionButNotDreaming_iconsVisible() {
         final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN a transition to dream has started but we're *not* dreaming
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 2bff0c6..1828da5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -160,6 +160,7 @@
 val Kosmos.shortcutHelperViewModel by
     Kosmos.Fixture {
         ShortcutHelperViewModel(
+            applicationContext,
             mockRoleManager,
             userTracker,
             applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
index fcd14d8..d8d4d2b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
@@ -20,12 +20,14 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.statusbar.chips.statusBarChipsLogger
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.ongoingCallInteractor
 
 val Kosmos.callChipInteractor: CallChipInteractor by
     Kosmos.Fixture {
         CallChipInteractor(
             scope = applicationCoroutineScope,
             repository = ongoingCallRepository,
+            ongoingCallInteractor = ongoingCallInteractor,
             logger = statusBarChipsLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
new file mode 100644
index 0000000..51fb36f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.phone.ongoingcall.domain.interactor
+
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+
+val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
+    Kosmos.Fixture {
+      OngoingCallInteractor(
+          scope = applicationCoroutineScope,
+          activeNotificationsInteractor = activeNotificationsInteractor,
+          activityManagerRepository = activityManagerRepository,
+          logBuffer = logcatLogBuffer("OngoingCallInteractorKosmos"),
+      )
+    }
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
index 26d8397..c1a1ee7 100644
--- a/packages/Vcn/service-b/Android.bp
+++ b/packages/Vcn/service-b/Android.bp
@@ -77,6 +77,7 @@
         "framework-connectivity-t-pre-jarjar",
         "framework-connectivity-b-pre-jarjar",
         "framework-wifi.stubs.module_lib",
+        "keepanno-annotations",
         "modules-utils-statemachine",
         "unsupportedappusage",
     ],
diff --git a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
new file mode 100644
index 0000000..02c8ce4
--- /dev/null
+++ b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+
+/**
+ * Service initializer for VCN. This is called by system server to create a new instance of
+ * VcnManagementService.
+ */
+// This class is reflectively invoked from SystemServer and ConnectivityServiceInitializer.
+// Without this annotation, this class will be treated as unused class and be removed during build
+// time.
+@UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+public final class ConnectivityServiceInitializerB extends SystemService {
+    private static final String TAG = ConnectivityServiceInitializerB.class.getSimpleName();
+    private final VcnManagementService mVcnManagementService;
+
+    public ConnectivityServiceInitializerB(Context context) {
+        super(context);
+        mVcnManagementService = VcnManagementService.create(context);
+    }
+
+    @Override
+    public void onStart() {
+        if (mVcnManagementService != null) {
+            Log.i(TAG, "Registering " + Context.VCN_MANAGEMENT_SERVICE);
+            publishBinderService(
+                    Context.VCN_MANAGEMENT_SERVICE,
+                    mVcnManagementService,
+                    /* allowIsolated= */ false);
+        }
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (mVcnManagementService != null && phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            Log.i(TAG, "Starting " + Context.VCN_MANAGEMENT_SERVICE);
+            mVcnManagementService.systemReady();
+        }
+    }
+}
diff --git a/services/Android.bp b/services/Android.bp
index 225304f..fc0bb33 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -294,10 +294,6 @@
         "service-permission.stubs.system_server",
         "service-rkp.stubs.system_server",
         "service-sdksandbox.stubs.system_server",
-
-        // TODO: b/30242953 This is for accessing IVcnManagementService and
-        // can be removed VCN is in mainline
-        "framework-connectivity-b-pre-jarjar",
     ],
 
     soong_config_variables: {
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 80e0e5d..b78d103 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -2,6 +2,13 @@
 container: "system"
 
 flag {
+  name: "autofill_session_destroyed"
+  namespace: "autofill"
+  description: "Guards against new metrics definitions introduced in W"
+  bug: "342676602"
+}
+
+flag {
   name: "autofill_w_metrics"
   namespace: "autofill"
   description: "Guards against new metrics definitions introduced in W"
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index cd4ace2..5cf96bf 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -61,6 +61,7 @@
 import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.FillEventHistory.Event.NoSaveReason;
 import android.service.autofill.FillResponse;
+import android.service.autofill.Flags;
 import android.service.autofill.IAutoFillService;
 import android.service.autofill.InlineSuggestionRenderService;
 import android.service.autofill.SaveInfo;
@@ -100,6 +101,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Random;
+
 /**
  * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
  * app's {@link IAutoFillService} implementation.
@@ -748,6 +750,22 @@
     @GuardedBy("mLock")
     void removeSessionLocked(int sessionId) {
         mSessions.remove(sessionId);
+        if (Flags.autofillSessionDestroyed()) {
+            if (sVerbose) {
+                Slog.v(
+                        TAG,
+                        "removeSessionLocked(): removed " + sessionId);
+            }
+            RemoteFillService remoteService =
+                    new RemoteFillService(
+                            getContext(),
+                            mInfo.getServiceInfo().getComponentName(),
+                            mUserId,
+                            /* callbacks= */ null,
+                            mMaster.isInstantServiceAllowed(),
+                            /* credentialAutofillService= */ null);
+            remoteService.onSessionDestroyed(null);
+        }
     }
 
     /**
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 07f5dcc..f1e8884 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -34,6 +34,7 @@
 import android.service.autofill.AutofillService;
 import android.service.autofill.ConvertCredentialRequest;
 import android.service.autofill.ConvertCredentialResponse;
+import android.service.autofill.FillEventHistory;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
@@ -497,6 +498,14 @@
                 }));
     }
 
+    public void onSessionDestroyed(@Nullable FillEventHistory history) {
+        boolean success = run(service -> {
+            service.onSessionDestroyed(history);
+        });
+
+        if (sVerbose) Slog.v(TAG, "called onSessionDestroyed(): " + success);
+    }
+
     void onSavedPasswordCountRequest(IResultReceiver receiver) {
         run(service -> service.onSavedPasswordCountRequest(receiver));
     }
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index fb527c1..2412b01 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -19,6 +19,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.Ringtone;
@@ -35,6 +36,7 @@
 import android.util.Pair;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
@@ -259,11 +261,19 @@
                     + mReportedDockState);
             final int previousDockState = mPreviousDockState;
             mPreviousDockState = mReportedDockState;
-            // Skip the dock intent if not yet provisioned.
+
             final ContentResolver cr = getContext().getContentResolver();
-            if (!mDeviceProvisionedObserver.isDeviceProvisioned()) {
-                Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
-                return;
+
+            /// If the allow dock rotation before provision is enabled then we allow rotation.
+            final Resources r = getContext().getResources();
+            final boolean allowDockBeforeProvision =
+                    r.getBoolean(R.bool.config_allowDockBeforeProvision);
+            if (!allowDockBeforeProvision) {
+                // Skip the dock intent if not yet provisioned.
+                if (!mDeviceProvisionedObserver.isDeviceProvisioned()) {
+                    Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
+                    return;
+                }
             }
 
             // Pack up the values and broadcast them to everyone
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index d13dd2f..896c9b8 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -885,8 +885,6 @@
                                 ? customModeType
                                 : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
                         mNightMode.set(mode);
-                        //deactivates AttentionMode if user toggles DarkTheme
-                        mAttentionModeThemeOverlay = MODE_ATTENTION_THEME_OVERLAY_OFF;
                         resetNightModeOverrideLocked();
                         persistNightMode(user);
                         // on screen off will update configuration instead
@@ -1009,15 +1007,16 @@
 
         @Override
         public boolean setNightModeActivatedForCustomMode(int modeNightCustomType, boolean active) {
-            return setNightModeActivatedForModeInternal(modeNightCustomType, active);
+            return setNightModeActivatedForModeInternal(modeNightCustomType, active, false);
         }
 
         @Override
         public boolean setNightModeActivated(boolean active) {
-            return setNightModeActivatedForModeInternal(mNightModeCustomType, active);
+            return setNightModeActivatedForModeInternal(mNightModeCustomType, active, true);
         }
 
-        private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) {
+        private boolean setNightModeActivatedForModeInternal(int modeCustomType,
+            boolean active, boolean isUserInteraction) {
             if (getContext().checkCallingOrSelfPermission(
                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -1053,13 +1052,16 @@
                         mOverrideNightModeOn = active;
                         mOverrideNightModeUser = mCurrentUser;
                         persistNightModeOverrides(mCurrentUser);
-                    } else if (mNightMode.get() == UiModeManager.MODE_NIGHT_NO
-                            && active) {
+                    } else if (mNightMode.get() == UiModeManager.MODE_NIGHT_NO && active) {
                         mNightMode.set(UiModeManager.MODE_NIGHT_YES);
-                    } else if (mNightMode.get() == UiModeManager.MODE_NIGHT_YES
-                            && !active) {
+                    } else if (mNightMode.get() == UiModeManager.MODE_NIGHT_YES && !active) {
                         mNightMode.set(UiModeManager.MODE_NIGHT_NO);
                     }
+
+                    if (isUserInteraction) {
+                        // deactivates AttentionMode if user toggles DarkTheme
+                        mAttentionModeThemeOverlay = MODE_ATTENTION_THEME_OVERLAY_OFF;
+                    }
                     updateConfigurationLocked();
                     applyConfigurationExternallyLocked();
                     persistNightMode(mCurrentUser);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 06c586f..295e044 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3191,7 +3191,7 @@
                     resolveProxyPackageName, proxyAttributionTag, proxyVirtualDeviceId,
                     Process.INVALID_UID, null, null,
                     Context.DEVICE_ID_DEFAULT, proxyFlags, !isProxyTrusted,
-                    "proxy " + message, shouldCollectMessage);
+                    "proxy " + message, shouldCollectMessage, 1);
             if (proxyReturn.getOpMode() != AppOpsManager.MODE_ALLOWED) {
                 return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag,
                         proxiedPackageName);
@@ -3210,7 +3210,20 @@
         return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
                 proxiedAttributionTag, proxiedVirtualDeviceId, proxyUid, resolveProxyPackageName,
                 proxyAttributionTag, proxyVirtualDeviceId, proxiedFlags, shouldCollectAsyncNotedOp,
-                message, shouldCollectMessage);
+                message, shouldCollectMessage, 1);
+    }
+
+    @Override
+    public void noteOperationsInBatch(Map batchedNoteOps) {
+        for (var entry : ((Map<AppOpsManager.NotedOp, Integer>) batchedNoteOps).entrySet()) {
+            AppOpsManager.NotedOp notedOp = entry.getKey();
+            int notedCount = entry.getValue();
+            mCheckOpsDelegateDispatcher.noteOperation(
+                    notedOp.getOp(), notedOp.getUid(), notedOp.getPackageName(),
+                    notedOp.getAttributionTag(), notedOp.getVirtualDeviceId(),
+                    notedOp.getShouldCollectAsyncNotedOp(), notedOp.getMessage(),
+                    notedOp.getShouldCollectMessage(), notedCount);
+        }
     }
 
     @Override
@@ -3228,7 +3241,7 @@
         }
         return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
                 attributionTag, Context.DEVICE_ID_DEFAULT, shouldCollectAsyncNotedOp, message,
-                shouldCollectMessage);
+                shouldCollectMessage, 1);
     }
 
     @Override
@@ -3237,13 +3250,12 @@
             String message, boolean shouldCollectMessage) {
         return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
                 attributionTag, virtualDeviceId, shouldCollectAsyncNotedOp, message,
-                shouldCollectMessage);
+                shouldCollectMessage, 1);
     }
 
     private SyncNotedAppOp noteOperationImpl(int code, int uid, @Nullable String packageName,
-             @Nullable String attributionTag, int virtualDeviceId,
-             boolean shouldCollectAsyncNotedOp, @Nullable String message,
-             boolean shouldCollectMessage) {
+            @Nullable String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
+            @Nullable String message, boolean shouldCollectMessage, int notedCount) {
         String resolvedPackageName;
         if (!shouldUseNewCheckOp()) {
             verifyIncomingUid(uid);
@@ -3278,14 +3290,14 @@
         return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
                 virtualDeviceId, Process.INVALID_UID, null, null,
                 Context.DEVICE_ID_DEFAULT, AppOpsManager.OP_FLAG_SELF, shouldCollectAsyncNotedOp,
-                message, shouldCollectMessage);
+                message, shouldCollectMessage, notedCount);
     }
 
     private SyncNotedAppOp noteOperationUnchecked(int code, int uid, @NonNull String packageName,
             @Nullable String attributionTag, int virtualDeviceId, int proxyUid,
             String proxyPackageName, @Nullable String proxyAttributionTag, int proxyVirtualDeviceId,
             @OpFlags int flags, boolean shouldCollectAsyncNotedOp, @Nullable String message,
-            boolean shouldCollectMessage) {
+            boolean shouldCollectMessage, int notedCount) {
         PackageVerificationResult pvr;
         try {
             pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
@@ -3388,11 +3400,11 @@
                     virtualDeviceId, flags, AppOpsManager.MODE_ALLOWED);
 
             attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
-                    getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags);
+                    getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags, notedCount);
 
             if (shouldCollectAsyncNotedOp) {
                 collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
-                        shouldCollectMessage);
+                        shouldCollectMessage, notedCount);
             }
 
             return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
@@ -3551,7 +3563,7 @@
      */
     private void collectAsyncNotedOp(int uid, @NonNull String packageName, int opCode,
             @Nullable String attributionTag, @OpFlags int flags, @NonNull String message,
-            boolean shouldCollectMessage) {
+            boolean shouldCollectMessage, int notedCount) {
         Objects.requireNonNull(message);
 
         int callingUid = Binder.getCallingUid();
@@ -3559,42 +3571,51 @@
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
-
-                RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
-                AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
-                        attributionTag, message, System.currentTimeMillis());
-                final boolean[] wasNoteForwarded = {false};
-
                 if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) != 0
                         && shouldCollectMessage) {
                     reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode,
                             attributionTag, message);
                 }
 
-                if (callbacks != null) {
+                Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
+                RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
+                if (callbacks == null) {
+                    return;
+                }
+
+                final boolean[] wasNoteForwarded = {false};
+                if (Flags.rateLimitBatchedNoteOpAsyncCallbacksEnabled()) {
+                    notedCount = 1;
+                }
+
+                for (int i = 0; i < notedCount; i++) {
+                    AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
+                            attributionTag, message, System.currentTimeMillis());
+                    wasNoteForwarded[0] = false;
                     callbacks.broadcast((cb) -> {
                         try {
                             cb.opNoted(asyncNotedOp);
                             wasNoteForwarded[0] = true;
                         } catch (RemoteException e) {
                             Slog.e(TAG,
-                                    "Could not forward noteOp of " + opCode + " to " + packageName
+                                    "Could not forward noteOp of " + opCode + " to "
+                                            + packageName
                                             + "/" + uid + "(" + attributionTag + ")", e);
                         }
                     });
-                }
 
-                if (!wasNoteForwarded[0]) {
-                    ArrayList<AsyncNotedAppOp> unforwardedOps = mUnforwardedAsyncNotedOps.get(key);
-                    if (unforwardedOps == null) {
-                        unforwardedOps = new ArrayList<>(1);
-                        mUnforwardedAsyncNotedOps.put(key, unforwardedOps);
-                    }
+                    if (!wasNoteForwarded[0]) {
+                        ArrayList<AsyncNotedAppOp> unforwardedOps = mUnforwardedAsyncNotedOps.get(
+                                key);
+                        if (unforwardedOps == null) {
+                            unforwardedOps = new ArrayList<>(1);
+                            mUnforwardedAsyncNotedOps.put(key, unforwardedOps);
+                        }
 
-                    unforwardedOps.add(asyncNotedOp);
-                    if (unforwardedOps.size() > MAX_UNFORWARDED_OPS) {
-                        unforwardedOps.remove(0);
+                        unforwardedOps.add(asyncNotedOp);
+                        if (unforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+                            unforwardedOps.remove(0);
+                        }
                     }
                 }
             }
@@ -4026,7 +4047,7 @@
 
         if (shouldCollectAsyncNotedOp && !isRestricted) {
             collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
-                    message, shouldCollectMessage);
+                    message, shouldCollectMessage, 1);
         }
 
         return new SyncNotedAppOp(isRestricted ? MODE_IGNORED : MODE_ALLOWED, code, attributionTag,
@@ -7574,34 +7595,36 @@
 
         public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
                 String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
-                String message, boolean shouldCollectMessage) {
+                String message, boolean shouldCollectMessage, int notedCount) {
             if (mPolicy != null) {
                 if (mCheckOpsDelegate != null) {
                     return mPolicy.noteOperation(code, uid, packageName, attributionTag,
                             virtualDeviceId, shouldCollectAsyncNotedOp, message,
-                            shouldCollectMessage, this::noteDelegateOperationImpl
+                            shouldCollectMessage, notedCount, this::noteDelegateOperationImpl
                     );
                 } else {
                     return mPolicy.noteOperation(code, uid, packageName, attributionTag,
                             virtualDeviceId, shouldCollectAsyncNotedOp, message,
-                            shouldCollectMessage, AppOpsService.this::noteOperationImpl
+                            shouldCollectMessage, notedCount, AppOpsService.this::noteOperationImpl
                     );
                 }
             } else if (mCheckOpsDelegate != null) {
                 return noteDelegateOperationImpl(code, uid, packageName, attributionTag,
-                        virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+                        virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                        notedCount);
             }
             return noteOperationImpl(code, uid, packageName, attributionTag,
-                    virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+                    virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                    notedCount);
         }
 
         private SyncNotedAppOp noteDelegateOperationImpl(int code, int uid,
                 @Nullable String packageName, @Nullable String featureId, int virtualDeviceId,
                 boolean shouldCollectAsyncNotedOp, @Nullable String message,
-                boolean shouldCollectMessage) {
+                boolean shouldCollectMessage, int notedCount) {
             return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
                     virtualDeviceId, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
-                    AppOpsService.this::noteOperationImpl
+                    notedCount, AppOpsService.this::noteOperationImpl
             );
         }
 
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 314664b..4d114b4 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -100,10 +100,12 @@
      * @param proxyDeviceId       The device Id of the proxy
      * @param uidState            UID state of the app noteOp/startOp was called for
      * @param flags               OpFlags of the call
+     * @param accessCount         The number of times the op is accessed
      */
     public void accessed(int proxyUid, @Nullable String proxyPackageName,
             @Nullable String proxyAttributionTag, @Nullable String proxyDeviceId,
-            @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) {
+            @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+            int accessCount) {
         long accessTime = System.currentTimeMillis();
         accessed(accessTime, -1, proxyUid, proxyPackageName, proxyAttributionTag, proxyDeviceId,
                 uidState, flags);
@@ -111,7 +113,7 @@
         mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                 parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
                 AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
-                DiscreteRegistry.ACCESS_TYPE_NOTE_OP);
+                DiscreteRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
     }
 
     /**
@@ -255,7 +257,7 @@
         if (isStarted) {
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                     parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
-                    attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP);
+                    attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP, 1);
         }
     }
 
@@ -451,7 +453,7 @@
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                     parent.packageName, persistentDeviceId, tag, event.getUidState(),
                     event.getFlags(), startTime, event.getAttributionFlags(),
-                    event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP);
+                    event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP, 1);
             if (shouldSendActive) {
                 mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                         parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 6b02538..5e67f26 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -475,7 +475,7 @@
             @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
             @OpFlags int flags, long accessTime,
             @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
-            @DiscreteRegistry.AccessType int accessType) {
+            @DiscreteRegistry.AccessType int accessType, int accessCount) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -484,7 +484,7 @@
                 }
                 getUpdatedPendingHistoricalOpsMLocked(
                         System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
-                        attributionTag, uidState, flags, 1);
+                        attributionTag, uidState, flags, accessCount);
 
                 mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
                         attributionTag, flags, uidState, accessTime, -1, attributionFlags,
diff --git a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
index 4736918..7502664 100644
--- a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
+++ b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
@@ -27,6 +27,7 @@
 import static android.Manifest.permission.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION;
 import static android.Manifest.permission.MODIFY_AUDIO_ROUTING;
 import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS;
+import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
 import static android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS;
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
 import static android.Manifest.permission.RECORD_AUDIO;
@@ -84,6 +85,8 @@
         MONITORED_PERMS[PermissionEnum.BLUETOOTH_CONNECT] = BLUETOOTH_CONNECT;
         MONITORED_PERMS[PermissionEnum.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION] =
                 BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION;
+        MONITORED_PERMS[PermissionEnum.MODIFY_AUDIO_SETTINGS_PRIVILEGED] =
+                MODIFY_AUDIO_SETTINGS_PRIVILEGED;
     }
 
     private final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index e9cb279..e989d68 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -40,7 +40,7 @@
 import com.android.internal.util.function.DodecFunction;
 import com.android.internal.util.function.HexConsumer;
 import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.NonaFunction;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.TriFunction;
 import com.android.internal.util.function.UndecFunction;
@@ -351,22 +351,22 @@
         @Override
         public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
                 @Nullable String featureId, int virtualDeviceId, boolean shouldCollectAsyncNotedOp,
-                @Nullable String message, boolean shouldCollectMessage,
-                @NonNull OctFunction<Integer, Integer, String, String, Integer, Boolean, String,
-                        Boolean, SyncNotedAppOp> superImpl) {
+                @Nullable String message, boolean shouldCollectMessage, int notedCount,
+                @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
+                                        Boolean, Integer, SyncNotedAppOp> superImpl) {
             if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
                         Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId,
-                            shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+                            shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
             return superImpl.apply(code, uid, packageName, featureId, virtualDeviceId,
-                    shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+                    shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index ecffd38..33210e2 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -53,7 +53,7 @@
 import com.android.internal.util.function.DodecFunction;
 import com.android.internal.util.function.HexConsumer;
 import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.NonaFunction;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.UndecFunction;
 import com.android.server.LocalServices;
@@ -246,11 +246,12 @@
     public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
             @Nullable String attributionTag, int virtualDeviceId,
             boolean shouldCollectAsyncNotedOp, @Nullable String message,
-            boolean shouldCollectMessage, @NonNull OctFunction<Integer, Integer, String, String,
-                    Integer, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
+            boolean shouldCollectMessage, int notedCount,
+            @NonNull NonaFunction<Integer, Integer, String, String,
+                    Integer, Boolean, String, Boolean, Integer, SyncNotedAppOp> superImpl) {
         return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag),
                 resolveUid(code, uid), packageName, attributionTag, virtualDeviceId,
-                shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+                shouldCollectAsyncNotedOp, message, shouldCollectMessage, notedCount);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
index 15c3099..1b6ce9d 100644
--- a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
@@ -119,6 +119,7 @@
 
     @Override
     public void finishSession() {
+        Slog.d(TAG, "Session finish requested, ending vibration session...");
         // Do not abort session in HAL, wait for ongoing vibration requests to complete.
         // This might take a while to end the session, but it can be aborted by cancelSession.
         requestEndSession(Status.FINISHED, /* shouldAbort= */ false, /* isVendorRequest= */ true);
@@ -126,6 +127,7 @@
 
     @Override
     public void cancelSession() {
+        Slog.d(TAG, "Session cancel requested, aborting vibration session...");
         // Always abort session in HAL while cancelling it.
         // This might be triggered after finishSession was already called.
         requestEndSession(Status.CANCELLED_BY_USER, /* shouldAbort= */ true,
@@ -228,13 +230,14 @@
     @Override
     public void notifySessionCallback() {
         synchronized (mLock) {
+            Slog.d(TAG, "Session callback received, ending vibration session...");
             // If end was not requested then the HAL has cancelled the session.
             maybeSetEndRequestLocked(Status.CANCELLED_BY_UNKNOWN_REASON,
                     /* isVendorRequest= */ false);
             maybeSetStatusToRequestedLocked();
             clearVibrationConductor();
+            mHandler.post(() -> mManagerHooks.onSessionReleased(mSessionId));
         }
-        mHandler.post(() -> mManagerHooks.onSessionReleased(mSessionId));
     }
 
     @Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fde6ce2..aa63c4a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -429,6 +429,8 @@
             "/apex/com.android.tethering/javalib/service-connectivity.jar";
     private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS =
             "com.android.server.ConnectivityServiceInitializer";
+    private static final String CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS =
+            "com.android.server.ConnectivityServiceInitializerB";
     private static final String NETWORK_STATS_SERVICE_INITIALIZER_CLASS =
             "com.android.server.NetworkStatsServiceInitializer";
     private static final String UWB_APEX_SERVICE_JAR_PATH =
@@ -1486,7 +1488,6 @@
         IStorageManager storageManager = null;
         NetworkManagementService networkManagement = null;
         VpnManagerService vpnManager = null;
-        VcnManagementService vcnManagement = null;
         NetworkPolicyManagerService networkPolicy = null;
         WindowManagerService wm = null;
         NetworkTimeUpdateService networkTimeUpdater = null;
@@ -2232,8 +2233,10 @@
 
             t.traceBegin("StartVcnManagementService");
             try {
-                vcnManagement = VcnManagementService.create(context);
-                ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
+                // TODO: b/375213246 When VCN is in mainline module, load it from the apex path.
+                // Whether VCN will be in apex or in the platform will be gated by a build system
+                // flag.
+                mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS);
             } catch (Throwable e) {
                 reportWtf("starting VCN Management Service", e);
             }
@@ -3159,7 +3162,6 @@
         final MediaRouterService mediaRouterF = mediaRouter;
         final MmsServiceBroker mmsServiceF = mmsService;
         final VpnManagerService vpnManagerF = vpnManager;
-        final VcnManagementService vcnManagementF = vcnManagement;
         final WindowManagerService windowManagerF = wm;
         final ConnectivityManager connectivityF = (ConnectivityManager)
                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -3286,15 +3288,6 @@
                 reportWtf("making VpnManagerService ready", e);
             }
             t.traceEnd();
-            t.traceBegin("MakeVcnManagementServiceReady");
-            try {
-                if (vcnManagementF != null) {
-                    vcnManagementF.systemReady();
-                }
-            } catch (Throwable e) {
-                reportWtf("making VcnManagementService ready", e);
-            }
-            t.traceEnd();
             t.traceBegin("MakeNetworkPolicyServiceReady");
             try {
                 if (networkPolicyF != null) {
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 3b0cb4a..0404b82 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -1460,9 +1460,12 @@
         verify(mInjector).startDreamWhenDockedIfAppropriate(mContext);
     }
 
-    private void testAttentionModeThemeOverlay(boolean modeNight) throws RemoteException {
+    // Test the attention mode overlay with all the possible attention modes and the initial night
+    // mode state. Also tests if the attention mode is turned off when the night mode is toggled by
+    // the user.
+    private void testAttentionModeThemeOverlay(boolean initialNightMode) throws RemoteException {
         //setup
-        if (modeNight) {
+        if (initialNightMode) {
             mService.setNightMode(MODE_NIGHT_YES);
             assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
         } else {
@@ -1472,21 +1475,29 @@
 
         // attention modes with expected night modes
         Map<Integer, Boolean> modes = Map.of(
-                MODE_ATTENTION_THEME_OVERLAY_OFF, modeNight,
+                MODE_ATTENTION_THEME_OVERLAY_OFF, initialNightMode,
                 MODE_ATTENTION_THEME_OVERLAY_DAY, false,
                 MODE_ATTENTION_THEME_OVERLAY_NIGHT, true
         );
 
         // test
-        for (int aMode : modes.keySet()) {
+        for (int attentionMode : modes.keySet()) {
             try {
-                mService.setAttentionModeThemeOverlay(aMode);
+                mService.setAttentionModeThemeOverlay(attentionMode);
 
                 int appliedAMode = mService.getAttentionModeThemeOverlay();
-                boolean nMode = modes.get(aMode);
+                boolean expectedNightMode = modes.get(attentionMode);
 
-                assertEquals(aMode, appliedAMode);
-                assertEquals(isNightModeActivated(), nMode);
+                assertEquals(attentionMode, appliedAMode);
+                assertEquals(expectedNightMode, isNightModeActivated());
+
+                // If attentionMode is active, flip the night mode and assets
+                // the attention mode is disabled
+                if (attentionMode != MODE_ATTENTION_THEME_OVERLAY_OFF) {
+                    mService.setNightModeActivated(!expectedNightMode);
+                    assertEquals(MODE_ATTENTION_THEME_OVERLAY_OFF,
+                            mService.getAttentionModeThemeOverlay());
+                }
             } catch (RemoteException e) {
                 fail("Error communicating with server: " + e.getMessage());
             }