Merge "Add OWNERS file for mockingservicetests backup directory"
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e928eb5..e6ddf9f 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -413,6 +413,7 @@
 
   public final class DeviceConfig {
     field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
+    field public static final String NAMESPACE_APP_CLONING = "app_cloning";
     field public static final String NAMESPACE_APP_STANDBY = "app_standby";
     field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ce1eff1..001ebf5c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2909,6 +2909,7 @@
     method @NonNull public java.util.Set<android.content.ComponentName> getBlockedCrossTaskNavigations();
     method public int getDefaultActivityPolicy();
     method public int getDefaultNavigationPolicy();
+    method public int getDevicePolicy(int);
     method public int getLockState();
     method @Nullable public String getName();
     method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
@@ -2916,14 +2917,18 @@
     field public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
     field public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
+    field public static final int DEVICE_POLICY_CUSTOM = 1; // 0x1
+    field public static final int DEVICE_POLICY_DEFAULT = 0; // 0x0
     field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
     field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
     field public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
     field public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
+    field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
   }
 
   public static final class VirtualDeviceParams.Builder {
     ctor public VirtualDeviceParams.Builder();
+    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder addDevicePolicy(int, int);
     method @NonNull public android.companion.virtual.VirtualDeviceParams build();
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 82d7534..7d6336a 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -52,6 +52,11 @@
     List<VirtualDevice> getVirtualDevices();
 
     /**
+     * Returns the device policy for the given virtual device and policy type.
+     */
+    int getDevicePolicy(int deviceId, int policyType);
+
+    /**
      * Creates a virtual display owned by a particular virtual device.
      *
      * @param virtualDisplayConfig The configuration used in creating the display
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 0bb86fb..c14bb1b 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -182,6 +182,28 @@
     }
 
     /**
+     * Returns the device policy for the given virtual device and policy type.
+     *
+     * <p>In case the virtual device identifier is not valid, or there's no explicitly specified
+     * policy for that device and policy type, then
+     * {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT} is returned.
+     *
+     * @hide
+     */
+    public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
+            int deviceId, @VirtualDeviceParams.PolicyType int policyType) {
+        if (mService == null) {
+            Log.w(TAG, "Failed to retrieve device policy; no virtual device manager service.");
+            return VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
+        }
+        try {
+            return mService.getDevicePolicy(deviceId, policyType);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * A virtual device has its own virtual display, audio output, microphone, and camera etc. The
      * creator of a virtual device can take the output from the virtual display and stream it over
      * to another device, and inject input events that are received from the remote device.
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index d40c9d6..c6e6f83 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -28,6 +28,7 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.SparseIntArray;
 
 import com.android.internal.util.Preconditions;
 
@@ -103,6 +104,47 @@
      */
     public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1;
 
+    /** @hide */
+    @IntDef(prefix = "DEVICE_POLICY_",  value = {DEVICE_POLICY_DEFAULT, DEVICE_POLICY_CUSTOM})
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+    public @interface DevicePolicy {}
+
+    /**
+     * Indicates that there is no special logic for this virtual device and it should be treated
+     * the same way as the default device, keeping the default behavior unchanged.
+     */
+    public static final int DEVICE_POLICY_DEFAULT = 0;
+
+    /**
+     * Indicates that there is custom logic, specific to this virtual device, which should be
+     * triggered instead of the default behavior.
+     */
+    public static final int DEVICE_POLICY_CUSTOM = 1;
+
+    /**
+     * Any relevant component must be able to interpret the correct meaning of a custom policy for
+     * a given policy type.
+     * @hide
+     */
+    @IntDef(prefix = "POLICY_TYPE_",  value = {POLICY_TYPE_SENSORS})
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+    public @interface PolicyType {}
+
+    /**
+     * Tells the sensor framework how to handle sensor requests from contexts associated with this
+     * virtual device, namely the sensors returned by
+     * {@link android.hardware.SensorManager#getSensorList}:
+     *
+     * <ul>
+     *     <li>{@link #DEVICE_POLICY_DEFAULT}: Return the sensors of the default device.
+     *     <li>{@link #DEVICE_POLICY_CUSTOM}: Return the sensors of the virtual device. Note that if
+     *     the virtual device did not create any virtual sensors, then an empty list is returned.
+     * </ul>
+     */
+    public static final int POLICY_TYPE_SENSORS = 0;
+
     private final int mLockState;
     @NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
     @NonNull private final ArraySet<ComponentName> mAllowedCrossTaskNavigations;
@@ -114,6 +156,8 @@
     @ActivityPolicy
     private final int mDefaultActivityPolicy;
     @Nullable private final String mName;
+    // Mapping of @PolicyType to @DevicePolicy
+    @NonNull private final SparseIntArray mDevicePolicies;
 
     private VirtualDeviceParams(
             @LockState int lockState,
@@ -124,12 +168,14 @@
             @NonNull Set<ComponentName> allowedActivities,
             @NonNull Set<ComponentName> blockedActivities,
             @ActivityPolicy int defaultActivityPolicy,
-            @Nullable String name) {
+            @Nullable String name,
+            @NonNull SparseIntArray devicePolicies) {
         Preconditions.checkNotNull(usersWithMatchingAccounts);
         Preconditions.checkNotNull(allowedCrossTaskNavigations);
         Preconditions.checkNotNull(blockedCrossTaskNavigations);
         Preconditions.checkNotNull(allowedActivities);
         Preconditions.checkNotNull(blockedActivities);
+        Preconditions.checkNotNull(devicePolicies);
 
         mLockState = lockState;
         mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
@@ -140,6 +186,7 @@
         mBlockedActivities = new ArraySet<>(blockedActivities);
         mDefaultActivityPolicy = defaultActivityPolicy;
         mName = name;
+        mDevicePolicies = devicePolicies;
     }
 
     @SuppressWarnings("unchecked")
@@ -153,6 +200,7 @@
         mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
         mDefaultActivityPolicy = parcel.readInt();
         mName = parcel.readString8();
+        mDevicePolicies = parcel.readSparseIntArray();
     }
 
     /**
@@ -258,6 +306,16 @@
         return mName;
     }
 
+    /**
+     * Returns the policy specified for this policy type, or {@link #DEVICE_POLICY_DEFAULT} if no
+     * policy for this type has been explicitly specified.
+     *
+     * @see Builder#addDevicePolicy
+     */
+    public @DevicePolicy int getDevicePolicy(@PolicyType int policyType) {
+        return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -274,6 +332,7 @@
         dest.writeArraySet(mBlockedActivities);
         dest.writeInt(mDefaultActivityPolicy);
         dest.writeString8(mName);
+        dest.writeSparseIntArray(mDevicePolicies);
     }
 
     @Override
@@ -285,6 +344,18 @@
             return false;
         }
         VirtualDeviceParams that = (VirtualDeviceParams) o;
+        final int devicePoliciesCount = mDevicePolicies.size();
+        if (devicePoliciesCount != that.mDevicePolicies.size()) {
+            return false;
+        }
+        for (int i = 0; i < devicePoliciesCount; i++) {
+            if (mDevicePolicies.keyAt(i) != that.mDevicePolicies.keyAt(i)) {
+                return false;
+            }
+            if (mDevicePolicies.valueAt(i) != that.mDevicePolicies.valueAt(i)) {
+                return false;
+            }
+        }
         return mLockState == that.mLockState
                 && mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts)
                 && Objects.equals(mAllowedCrossTaskNavigations, that.mAllowedCrossTaskNavigations)
@@ -298,10 +369,15 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(
+        int hashCode = Objects.hash(
                 mLockState, mUsersWithMatchingAccounts, mAllowedCrossTaskNavigations,
                 mBlockedCrossTaskNavigations, mDefaultNavigationPolicy,  mAllowedActivities,
-                mBlockedActivities, mDefaultActivityPolicy, mName);
+                mBlockedActivities, mDefaultActivityPolicy, mName, mDevicePolicies);
+        for (int i = 0; i < mDevicePolicies.size(); i++) {
+            hashCode = 31 * hashCode + mDevicePolicies.keyAt(i);
+            hashCode = 31 * hashCode + mDevicePolicies.valueAt(i);
+        }
+        return hashCode;
     }
 
     @Override
@@ -317,6 +393,7 @@
                 + " mBlockedActivities=" + mBlockedActivities
                 + " mDefaultActivityPolicy=" + mDefaultActivityPolicy
                 + " mName=" + mName
+                + " mDevicePolicies=" + mDevicePolicies
                 + ")";
     }
 
@@ -350,6 +427,7 @@
         private int mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED;
         private boolean mDefaultActivityPolicyConfigured = false;
         @Nullable private String mName;
+        @NonNull private SparseIntArray mDevicePolicies = new SparseIntArray();
 
         /**
          * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
@@ -528,6 +606,18 @@
         }
 
         /**
+         * Specifies a policy for this virtual device.
+         *
+         * @param policyType the type of policy, i.e. which behavior to specify a policy for.
+         * @param devicePolicy the value of the policy, i.e. how to interpret the device behavior.
+         */
+        @NonNull
+        public Builder addDevicePolicy(@PolicyType int policyType, @DevicePolicy int devicePolicy) {
+            mDevicePolicies.put(policyType, devicePolicy);
+            return this;
+        }
+
+        /**
          * Builds the {@link VirtualDeviceParams} instance.
          */
         @NonNull
@@ -541,7 +631,8 @@
                     mAllowedActivities,
                     mBlockedActivities,
                     mDefaultActivityPolicy,
-                    mName);
+                    mName,
+                    mDevicePolicies);
         }
     }
 }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 7095d1b..6eeb5e7 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -131,6 +131,13 @@
     public static final String NAMESPACE_APP_STANDBY = "app_standby";
 
     /**
+     * Namespace for all App Cloning related features.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final String NAMESPACE_APP_CLONING = "app_cloning";
+
+    /**
      * Namespace for AttentionManagerService related features.
      *
      * @hide
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b24303b..720813a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1073,7 +1073,7 @@
     private void handleSyncBufferCallback(SurfaceHolder.Callback[] callbacks,
             SyncBufferTransactionCallback syncBufferTransactionCallback) {
 
-        getViewRootImpl().addToSync(syncBufferCallback ->
+        getViewRootImpl().addToSync((parentSyncGroup, syncBufferCallback) ->
                 redrawNeededAsync(callbacks, () -> {
                     Transaction t = null;
                     if (mBlastBufferQueue != null) {
@@ -1081,7 +1081,7 @@
                         t = syncBufferTransactionCallback.waitForTransaction();
                     }
 
-                    syncBufferCallback.onBufferReady(t);
+                    syncBufferCallback.onTransactionReady(t);
                     onDrawFinished();
                 }));
     }
@@ -1092,9 +1092,9 @@
             mSyncGroups.add(syncGroup);
         }
 
-        syncGroup.addToSync(syncBufferCallback -> redrawNeededAsync(callbacks,
-                () -> {
-                    syncBufferCallback.onBufferReady(null);
+        syncGroup.addToSync((parentSyncGroup, syncBufferCallback) ->
+                redrawNeededAsync(callbacks, () -> {
+                    syncBufferCallback.onTransactionReady(null);
                     onDrawFinished();
                     synchronized (mSyncGroups) {
                         mSyncGroups.remove(syncGroup);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7e8ebd7..5e1dc34 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -231,6 +231,7 @@
 import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -851,7 +852,7 @@
     }
 
     private SurfaceSyncGroup mSyncGroup;
-    private SurfaceSyncGroup.SyncBufferCallback mSyncBufferCallback;
+    private SurfaceSyncGroup.TransactionReadyCallback mTransactionReadyCallback;
     private int mNumSyncsInProgress = 0;
 
     private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
@@ -3610,8 +3611,8 @@
                 mPendingTransitions.clear();
             }
 
-            if (mSyncBufferCallback != null) {
-                mSyncBufferCallback.onBufferReady(null);
+            if (mTransactionReadyCallback != null) {
+                mTransactionReadyCallback.onTransactionReady(null);
             }
         } else if (cancelAndRedraw) {
             mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
@@ -3626,8 +3627,8 @@
                 }
                 mPendingTransitions.clear();
             }
-            if (!performDraw() && mSyncBufferCallback != null) {
-                mSyncBufferCallback.onBufferReady(null);
+            if (!performDraw() && mTransactionReadyCallback != null) {
+                mTransactionReadyCallback.onTransactionReady(null);
             }
         }
 
@@ -3641,7 +3642,7 @@
         if (!cancelAndRedraw) {
             mReportNextDraw = false;
             mLastReportNextDrawReason = null;
-            mSyncBufferCallback = null;
+            mTransactionReadyCallback = null;
             mSyncBuffer = false;
             if (isInLocalSync()) {
                 mSyncGroup.markSyncReady();
@@ -4388,7 +4389,7 @@
             return false;
         }
 
-        final boolean fullRedrawNeeded = mFullRedrawNeeded || mSyncBufferCallback != null;
+        final boolean fullRedrawNeeded = mFullRedrawNeeded || mTransactionReadyCallback != null;
         mFullRedrawNeeded = false;
 
         mIsDrawing = true;
@@ -4396,9 +4397,9 @@
 
         addFrameCommitCallbackIfNeeded();
 
-        boolean usingAsyncReport = isHardwareEnabled() && mSyncBufferCallback != null;
+        boolean usingAsyncReport = isHardwareEnabled() && mTransactionReadyCallback != null;
         if (usingAsyncReport) {
-            registerCallbacksForSync(mSyncBuffer, mSyncBufferCallback);
+            registerCallbacksForSync(mSyncBuffer, mTransactionReadyCallback);
         } else if (mHasPendingTransactions) {
             // These callbacks are only needed if there's no sync involved and there were calls to
             // applyTransactionOnDraw. These callbacks check if the draw failed for any reason and
@@ -4449,10 +4450,11 @@
             }
 
             if (mSurfaceHolder != null && mSurface.isValid()) {
-                final SurfaceSyncGroup.SyncBufferCallback syncBufferCallback = mSyncBufferCallback;
+                final SurfaceSyncGroup.TransactionReadyCallback transactionReadyCallback =
+                        mTransactionReadyCallback;
                 SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() ->
-                        mHandler.post(() -> syncBufferCallback.onBufferReady(null)));
-                mSyncBufferCallback = null;
+                        mHandler.post(() -> transactionReadyCallback.onTransactionReady(null)));
+                mTransactionReadyCallback = null;
 
                 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
 
@@ -4463,8 +4465,8 @@
                 }
             }
         }
-        if (mSyncBufferCallback != null && !usingAsyncReport) {
-            mSyncBufferCallback.onBufferReady(null);
+        if (mTransactionReadyCallback != null && !usingAsyncReport) {
+            mTransactionReadyCallback.onTransactionReady(null);
         }
         if (mPerformContentCapture) {
             performContentCaptureInitialReport();
@@ -11134,7 +11136,7 @@
     }
 
     private void registerCallbacksForSync(boolean syncBuffer,
-            final SurfaceSyncGroup.SyncBufferCallback syncBufferCallback) {
+            final SurfaceSyncGroup.TransactionReadyCallback transactionReadyCallback) {
         if (!isHardwareEnabled()) {
             return;
         }
@@ -11161,7 +11163,7 @@
                 // pendingDrawFinished.
                 if ((syncResult
                         & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
-                    syncBufferCallback.onBufferReady(
+                    transactionReadyCallback.onTransactionReady(
                             mBlastBufferQueue.gatherPendingTransactions(frame));
                     return null;
                 }
@@ -11171,7 +11173,8 @@
                 }
 
                 if (syncBuffer) {
-                    mBlastBufferQueue.syncNextTransaction(syncBufferCallback::onBufferReady);
+                    mBlastBufferQueue.syncNextTransaction(
+                            transactionReadyCallback::onTransactionReady);
                 }
 
                 return didProduceBuffer -> {
@@ -11191,7 +11194,7 @@
                         // since the frame didn't draw on this vsync. It's possible the frame will
                         // draw later, but it's better to not be sync than to block on a frame that
                         // may never come.
-                        syncBufferCallback.onBufferReady(
+                        transactionReadyCallback.onTransactionReady(
                                 mBlastBufferQueue.gatherPendingTransactions(frame));
                         return;
                     }
@@ -11200,22 +11203,49 @@
                     // syncNextTransaction callback. Instead, just report back to the Syncer so it
                     // knows that this sync request is complete.
                     if (!syncBuffer) {
-                        syncBufferCallback.onBufferReady(null);
+                        transactionReadyCallback.onTransactionReady(null);
                     }
                 };
             }
         });
     }
 
+    private final Executor mPostAtFrontExecutor = new Executor() {
+        @Override
+        public void execute(Runnable command) {
+            mHandler.postAtFrontOfQueue(command);
+        }
+    };
+
     public final SurfaceSyncGroup.SyncTarget mSyncTarget = new SurfaceSyncGroup.SyncTarget() {
         @Override
-        public void onReadyToSync(SurfaceSyncGroup.SyncBufferCallback syncBufferCallback) {
-            readyToSync(syncBufferCallback);
+        public void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
+                SurfaceSyncGroup.TransactionReadyCallback transactionReadyCallback) {
+            updateSyncInProgressCount(parentSyncGroup);
+            if (!isInLocalSync()) {
+                // Always sync the buffer if the sync request did not come from VRI.
+                mSyncBuffer = true;
+            }
+            if (mAttachInfo.mThreadedRenderer != null) {
+                HardwareRenderer.setRtAnimationsEnabled(false);
+            }
+
+            if (mTransactionReadyCallback != null) {
+                Log.d(mTag, "Already set sync for the next draw.");
+                mTransactionReadyCallback.onTransactionReady(null);
+            }
+            if (DEBUG_BLAST) {
+                Log.d(mTag, "Setting syncFrameCallback");
+            }
+            mTransactionReadyCallback = transactionReadyCallback;
+            if (!mIsInTraversal && !mTraversalScheduled) {
+                scheduleTraversals();
+            }
         }
 
-        @Override
-        public void onSyncComplete() {
-            mHandler.postAtFrontOfQueue(() -> {
+        private void updateSyncInProgressCount(SurfaceSyncGroup parentSyncGroup) {
+            mNumSyncsInProgress++;
+            parentSyncGroup.addSyncCompleteCallback(mPostAtFrontExecutor, () -> {
                 if (--mNumSyncsInProgress == 0 && mAttachInfo.mThreadedRenderer != null) {
                     HardwareRenderer.setRtAnimationsEnabled(true);
                 }
@@ -11228,29 +11258,6 @@
         return mSyncTarget;
     }
 
-    private void readyToSync(SurfaceSyncGroup.SyncBufferCallback syncBufferCallback) {
-        mNumSyncsInProgress++;
-        if (!isInLocalSync()) {
-            // Always sync the buffer if the sync request did not come from VRI.
-            mSyncBuffer = true;
-        }
-        if (mAttachInfo.mThreadedRenderer != null) {
-            HardwareRenderer.setRtAnimationsEnabled(false);
-        }
-
-        if (mSyncBufferCallback != null) {
-            Log.d(mTag, "Already set sync for the next draw.");
-            mSyncBufferCallback.onBufferReady(null);
-        }
-        if (DEBUG_BLAST) {
-            Log.d(mTag, "Setting syncFrameCallback");
-        }
-        mSyncBufferCallback = syncBufferCallback;
-        if (!mIsInTraversal && !mTraversalScheduled) {
-            scheduleTraversals();
-        }
-    }
-
     void mergeSync(SurfaceSyncGroup otherSyncGroup) {
         if (!isInLocalSync()) {
             return;
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 4248096..3950739 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -57,12 +57,13 @@
  * option is provided.
  *
  * The following is what happens within the {@link SurfaceSyncGroup}
- * 1. Each SyncTarget will get a {@link SyncTarget#onReadyToSync} callback that contains a
- * {@link SyncBufferCallback}.
- * 2. Each {@link SyncTarget} needs to invoke {@link SyncBufferCallback#onBufferReady(Transaction)}.
- * This makes sure the SurfaceSyncGroup knows when the SyncTarget is complete, allowing the
- * SurfaceSyncGroup to get the Transaction that contains the buffer.
- * 3. When the final SyncBufferCallback finishes for the SurfaceSyncGroup, in most cases the
+ * 1. Each SyncTarget will get a {@link SyncTarget#onAddedToSyncGroup} callback that contains a
+ * {@link TransactionReadyCallback}.
+ * 2. Each {@link SyncTarget} needs to invoke
+ * {@link TransactionReadyCallback#onTransactionReady(Transaction)}. This makes sure the
+ * SurfaceSyncGroup knows when the SyncTarget is complete, allowing the SurfaceSyncGroup to get the
+ * Transaction that contains the buffer.
+ * 3. When the final TransactionReadyCallback finishes for the SurfaceSyncGroup, in most cases the
  * transaction is applied and then the sync complete callbacks are invoked, letting the callers know
  * the sync is now complete.
  *
@@ -86,8 +87,6 @@
     private final Transaction mTransaction = sTransactionFactory.get();
     @GuardedBy("mLock")
     private boolean mSyncReady;
-    @GuardedBy("mLock")
-    private final Set<SyncTarget> mSyncTargets = new ArraySet<>();
 
     @GuardedBy("mLock")
     private Consumer<Transaction> mSyncRequestCompleteCallback;
@@ -197,14 +196,13 @@
      * Add a {@link SyncTarget} to a sync set. The sync set will wait for all
      * SyncableSurfaces to complete before notifying.
      *
-     * @param syncTarget A SyncableSurface that implements how to handle syncing
-     *                   buffers.
+     * @param syncTarget A SyncTarget that implements how to handle syncing transactions.
      * @return true if the SyncTarget was successfully added to the SyncGroup, false otherwise.
      */
     public boolean addToSync(SyncTarget syncTarget) {
-        SyncBufferCallback syncBufferCallback = new SyncBufferCallback() {
+        TransactionReadyCallback transactionReadyCallback = new TransactionReadyCallback() {
             @Override
-            public void onBufferReady(Transaction t) {
+            public void onTransactionReady(Transaction t) {
                 synchronized (mLock) {
                     if (t != null) {
                         mTransaction.merge(t);
@@ -221,10 +219,9 @@
                         + "SyncTargets can be added.");
                 return false;
             }
-            mPendingSyncs.add(syncBufferCallback.hashCode());
-            mSyncTargets.add(syncTarget);
+            mPendingSyncs.add(transactionReadyCallback.hashCode());
         }
-        syncTarget.onReadyToSync(syncBufferCallback);
+        syncTarget.onAddedToSyncGroup(this, transactionReadyCallback);
         return true;
     }
 
@@ -256,17 +253,13 @@
             Log.d(TAG, "Successfully finished sync id=" + this);
         }
 
-        for (SyncTarget syncTarget : mSyncTargets) {
-            syncTarget.onSyncComplete();
-        }
-        mSyncTargets.clear();
         mSyncRequestCompleteCallback.accept(mTransaction);
         mFinished = true;
     }
 
     /**
      * Add a Transaction to this sync set. This allows the caller to provide other info that
-     * should be synced with the buffers.
+     * should be synced with the transactions.
      */
     public void addTransactionToSync(Transaction t) {
         synchronized (mLock) {
@@ -334,9 +327,10 @@
         }
 
         @Override
-        public void onReadyToSync(SyncBufferCallback syncBufferCallback) {
+        public void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
+                TransactionReadyCallback transactionReadyCallback) {
             mFrameCallbackConsumer.accept(
-                    () -> mSurfaceView.syncNextFrame(syncBufferCallback::onBufferReady));
+                    () -> mSurfaceView.syncNextFrame(transactionReadyCallback::onTransactionReady));
         }
     }
 
@@ -345,22 +339,19 @@
      */
     public interface SyncTarget {
         /**
-         * Called when the Syncable is ready to begin handing a sync request. When invoked, the
-         * implementor is required to call {@link SyncBufferCallback#onBufferReady(Transaction)}
-         * and {@link SyncBufferCallback#onBufferReady(Transaction)} in order for this Syncable
-         * to be marked as complete.
+         * Called when the SyncTarget has been added to a SyncGroup as is ready to begin handing a
+         * sync request. When invoked, the implementor is required to call
+         * {@link TransactionReadyCallback#onTransactionReady(Transaction)} in order for this
+         * SurfaceSyncGroup to fully complete.
          *
          * Always invoked on the thread that initiated the call to {@link #addToSync(SyncTarget)}
          *
-         * @param syncBufferCallback A SyncBufferCallback that the caller must invoke onBufferReady
+         * @param parentSyncGroup The sync group this target has been added to.
+         * @param transactionReadyCallback A TransactionReadyCallback that the caller must invoke
+         *                                 onTransactionReady
          */
-        void onReadyToSync(SyncBufferCallback syncBufferCallback);
-
-        /**
-         * There's no guarantee about the thread this callback is invoked on.
-         */
-        default void onSyncComplete() {
-        }
+        void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
+                TransactionReadyCallback transactionReadyCallback);
     }
 
     /**
@@ -368,14 +359,14 @@
      * completed. The caller should invoke the calls when the rendering has started and finished a
      * frame.
      */
-    public interface SyncBufferCallback {
+    public interface TransactionReadyCallback {
         /**
-         * Invoked when the transaction contains the buffer and is ready to sync.
+         * Invoked when the transaction is ready to sync.
          *
-         * @param t The transaction that contains the buffer to be synced. This can be null if
-         *          there's nothing to sync
+         * @param t The transaction that contains the anything to be included in the synced. This
+         *          can be null if there's nothing to sync
          */
-        void onBufferReady(@Nullable Transaction t);
+        void onTransactionReady(@Nullable Transaction t);
     }
 
     /**
diff --git a/core/res/res/anim/dock_bottom_enter.xml b/core/res/res/anim/dock_bottom_enter.xml
deleted file mode 100644
index bfb97b6..0000000
--- a/core/res/res/anim/dock_bottom_enter.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2012, 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.
-*/
--->
-
-<!-- Animation for when a dock window at the bottom of the screen is entering. -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@android:interpolator/decelerate_cubic">
-    <translate android:fromYDelta="100%" android:toYDelta="0"
-        android:duration="@integer/dock_enter_exit_duration"/>
-</set>
diff --git a/core/res/res/anim/dock_bottom_exit.xml b/core/res/res/anim/dock_bottom_exit.xml
deleted file mode 100644
index 4e15448..0000000
--- a/core/res/res/anim/dock_bottom_exit.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2012, 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.
-*/
--->
-
-<!-- Animation for when a dock window at the bottom of the screen is exiting. -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@android:interpolator/accelerate_cubic">
-    <translate android:fromYDelta="0" android:toYDelta="100%"
-        android:startOffset="100" android:duration="@integer/dock_enter_exit_duration"/>
-</set>
diff --git a/core/res/res/anim/dock_bottom_exit_keyguard.xml b/core/res/res/anim/dock_bottom_exit_keyguard.xml
deleted file mode 100644
index 4de3ce5..0000000
--- a/core/res/res/anim/dock_bottom_exit_keyguard.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Copyright (C) 2016 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
-  -->
-
-<!-- Animation for when a dock window at the bottom of the screen is exiting while on Keyguard -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:interpolator="@android:interpolator/fast_out_linear_in">
-    <translate android:fromYDelta="0" android:toYDelta="100%"
-        android:duration="200"/>
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim/dock_left_enter.xml b/core/res/res/anim/dock_left_enter.xml
deleted file mode 100644
index 7f5dfd5..0000000
--- a/core/res/res/anim/dock_left_enter.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2012, 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.
-*/
--->
-
-<!-- Animation for when a dock window at the left of the screen is entering. -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@android:interpolator/decelerate_cubic">
-    <translate android:fromXDelta="-100%" android:toXDelta="0"
-        android:duration="250"/>
-</set>
diff --git a/core/res/res/anim/dock_left_exit.xml b/core/res/res/anim/dock_left_exit.xml
deleted file mode 100644
index 11cbc0b3..0000000
--- a/core/res/res/anim/dock_left_exit.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2012, 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.
-*/
--->
-
-<!-- Animation for when a dock window at the right of the screen is exiting. -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@android:interpolator/accelerate_cubic">
-    <translate android:fromXDelta="0" android:toXDelta="-100%"
-        android:startOffset="100" android:duration="250"/>
-</set>
diff --git a/core/res/res/anim/dock_right_enter.xml b/core/res/res/anim/dock_right_enter.xml
deleted file mode 100644
index a92c7d2..0000000
--- a/core/res/res/anim/dock_right_enter.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2012, 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.
-*/
--->
-
-<!-- Animation for when a dock window at the right of the screen is entering. -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@android:interpolator/decelerate_cubic">
-    <translate android:fromXDelta="100%" android:toXDelta="0"
-        android:duration="250"/>
-</set>
diff --git a/core/res/res/anim/dock_right_exit.xml b/core/res/res/anim/dock_right_exit.xml
deleted file mode 100644
index 80e4dc3..0000000
--- a/core/res/res/anim/dock_right_exit.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2012, 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.
-*/
--->
-
-<!-- Animation for when a dock window at the right of the screen is exiting. -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@android:interpolator/accelerate_cubic">
-    <translate android:fromXDelta="0" android:toXDelta="100%"
-        android:startOffset="100" android:duration="250"/>
-</set>
diff --git a/core/res/res/anim/dock_top_enter.xml b/core/res/res/anim/dock_top_enter.xml
deleted file mode 100644
index f763fb5..0000000
--- a/core/res/res/anim/dock_top_enter.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2007, 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.
-*/
--->
-
-<!-- Animation for when a dock window at the top of the screen is entering. -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@android:interpolator/decelerate_cubic">
-    <translate android:fromYDelta="-100%" android:toYDelta="0"
-        android:duration="@integer/dock_enter_exit_duration"/>
-</set>
diff --git a/core/res/res/anim/dock_top_exit.xml b/core/res/res/anim/dock_top_exit.xml
deleted file mode 100644
index 995b7d0..0000000
--- a/core/res/res/anim/dock_top_exit.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2007, 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.
-*/
--->
-
-<!-- Animation for when a dock window at the top of the screen is exiting. -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@android:interpolator/accelerate_cubic">
-    <translate android:fromYDelta="0" android:toYDelta="-100%"
-        android:startOffset="100" android:duration="@integer/dock_enter_exit_duration"/>
-</set>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 89ec5ba..437dedc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1691,15 +1691,6 @@
 
   <!-- From android.policy -->
   <java-symbol type="anim" name="app_starting_exit" />
-  <java-symbol type="anim" name="dock_top_enter" />
-  <java-symbol type="anim" name="dock_top_exit" />
-  <java-symbol type="anim" name="dock_bottom_enter" />
-  <java-symbol type="anim" name="dock_bottom_exit" />
-  <java-symbol type="anim" name="dock_bottom_exit_keyguard" />
-  <java-symbol type="anim" name="dock_left_enter" />
-  <java-symbol type="anim" name="dock_left_exit" />
-  <java-symbol type="anim" name="dock_right_enter" />
-  <java-symbol type="anim" name="dock_right_exit" />
   <java-symbol type="anim" name="fade_in" />
   <java-symbol type="anim" name="fade_out" />
   <java-symbol type="anim" name="voice_activity_close_exit" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 07cd7d6..f811940 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -116,7 +116,7 @@
     private final TouchTracker mTouchTracker = new TouchTracker();
 
     private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
-
+    @Nullable
     private IOnBackInvokedCallback mActiveCallback;
 
     @VisibleForTesting
@@ -180,6 +180,10 @@
     }
 
     private void initBackAnimationRunners() {
+        if (!IS_U_ANIMATION_ENABLED) {
+            return;
+        }
+
         final CrossTaskBackAnimation crossTaskAnimation = new CrossTaskBackAnimation(mContext);
         mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_TASK,
                 new BackAnimationRunner(crossTaskAnimation.mCallback, crossTaskAnimation.mRunner));
@@ -207,7 +211,7 @@
     private void updateEnableAnimationFromSetting() {
         int settingValue = Global.getInt(mContext.getContentResolver(),
                 Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);
-        boolean isEnabled = settingValue == SETTING_VALUE_ON && IS_U_ANIMATION_ENABLED;
+        boolean isEnabled = settingValue == SETTING_VALUE_ON;
         mEnableAnimations.set(isEnabled);
         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s", isEnabled);
     }
@@ -350,10 +354,14 @@
             return;
         }
         final int backType = backNavigationInfo.getType();
-        final boolean shouldDispatchToAnimator = shouldDispatchToAnimator(backType);
+        final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
         if (shouldDispatchToAnimator) {
-            mActiveCallback = mAnimationDefinition.get(backType).getCallback();
-            mAnimationDefinition.get(backType).startGesture();
+            if (mAnimationDefinition.contains(backType)) {
+                mActiveCallback = mAnimationDefinition.get(backType).getCallback();
+                mAnimationDefinition.get(backType).startGesture();
+            } else {
+                mActiveCallback = null;
+            }
         } else {
             mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
             dispatchOnBackStarted(mActiveCallback, mTouchTracker.createStartEvent(null));
@@ -361,9 +369,11 @@
     }
 
     private void onMove() {
-        if (!mBackGestureStarted || mBackNavigationInfo == null || !mEnableAnimations.get()) {
+        if (!mBackGestureStarted || mBackNavigationInfo == null || !mEnableAnimations.get()
+                || mActiveCallback == null) {
             return;
         }
+
         final BackEvent backEvent = mTouchTracker.createProgressEvent();
         dispatchOnBackProgressed(mActiveCallback, backEvent);
     }
@@ -387,11 +397,10 @@
         }
     }
 
-    private boolean shouldDispatchToAnimator(int backType) {
+    private boolean shouldDispatchToAnimator() {
         return mEnableAnimations.get()
                 && mBackNavigationInfo != null
-                && mBackNavigationInfo.isPrepareRemoteAnimation()
-                && mAnimationDefinition.contains(backType);
+                && mBackNavigationInfo.isPrepareRemoteAnimation();
     }
 
     private void dispatchOnBackStarted(IOnBackInvokedCallback callback,
@@ -461,6 +470,30 @@
         mTouchTracker.setProgressThreshold(progressThreshold);
     }
 
+    private void invokeOrCancelBack() {
+        // Make a synchronized call to core before dispatch back event to client side.
+        // If the close transition happens before the core receives onAnimationFinished, there will
+        // play a second close animation for that transition.
+        if (mBackAnimationFinishedCallback != null) {
+            try {
+                mBackAnimationFinishedCallback.onAnimationFinished(mTriggerBack);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed call IBackAnimationFinishedCallback", e);
+            }
+            mBackAnimationFinishedCallback = null;
+        }
+
+        if (mBackNavigationInfo != null) {
+            final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
+            if (mTriggerBack) {
+                dispatchOnBackInvoked(callback);
+            } else {
+                dispatchOnBackCancelled(callback);
+            }
+        }
+        finishBackNavigation();
+    }
+
     /**
      * Called when the gesture is released, then it could start the post commit animation.
      */
@@ -493,15 +526,9 @@
         }
 
         final int backType = mBackNavigationInfo.getType();
-        // Directly finish back navigation if no animator defined.
-        if (!shouldDispatchToAnimator(backType)) {
-            if (mTriggerBack) {
-                dispatchOnBackInvoked(mActiveCallback);
-            } else {
-                dispatchOnBackCancelled(mActiveCallback);
-            }
-            // Animation missing. Simply finish back navigation.
-            finishBackNavigation();
+        // Simply trigger and finish back navigation when no animator defined.
+        if (!shouldDispatchToAnimator() || mActiveCallback == null) {
+            invokeOrCancelBack();
             return;
         }
 
@@ -549,16 +576,7 @@
         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()");
 
         // Trigger the real back.
-        if (mBackNavigationInfo != null) {
-            IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
-            if (mTriggerBack) {
-                dispatchOnBackInvoked(callback);
-            } else {
-                dispatchOnBackCancelled(callback);
-            }
-        }
-
-        finishBackNavigation();
+        invokeOrCancelBack();
     }
 
     /**
@@ -567,25 +585,14 @@
     @VisibleForTesting
     void finishBackNavigation() {
         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
-        BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
-        boolean triggerBack = mTriggerBack;
-        mBackNavigationInfo = null;
-        mTriggerBack = false;
         mShouldStartOnNextMoveEvent = false;
         mTouchTracker.reset();
         mActiveCallback = null;
-        if (backNavigationInfo == null) {
-            return;
+        if (mBackNavigationInfo != null) {
+            mBackNavigationInfo.onBackNavigationFinished(mTriggerBack);
+            mBackNavigationInfo = null;
         }
-        if (mBackAnimationFinishedCallback != null) {
-            try {
-                mBackAnimationFinishedCallback.onAnimationFinished(triggerBack);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed call IBackAnimationFinishedCallback", e);
-            }
-            mBackAnimationFinishedCallback = null;
-        }
-        backNavigationInfo.onBackNavigationFinished(triggerBack);
+        mTriggerBack = false;
     }
 
     private void createAdapter() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index b603e03..d75c36c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -38,6 +38,7 @@
 import android.content.pm.ApplicationInfo;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteCallback;
@@ -56,6 +57,7 @@
 import android.window.IBackAnimationFinishedCallback;
 import android.window.IOnBackInvokedCallback;
 
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -189,31 +191,23 @@
         }
 
         for (int type: testTypes) {
-            boolean[] backNavigationDone = new boolean[]{false};
-            boolean[] triggerBack = new boolean[]{false};
-
+            final ResultListener result  = new ResultListener();
             createNavigationInfo(new BackNavigationInfo.Builder()
                     .setType(type)
                     .setOnBackInvokedCallback(mAppCallback)
                     .setPrepareRemoteAnimation(true)
-                    .setOnBackNavigationDone(
-                            new RemoteCallback(result -> {
-                                backNavigationDone[0] = true;
-                                triggerBack[0] = result.getBoolean(KEY_TRIGGER_BACK);
-                            })));
+                    .setOnBackNavigationDone(new RemoteCallback(result)));
             triggerBackGesture();
             simulateRemoteAnimationStart(type);
             simulateRemoteAnimationFinished();
             mShellExecutor.flushAll();
 
             assertTrue("Navigation Done callback not called for "
-                    + BackNavigationInfo.typeToString(type), backNavigationDone[0]);
-            assertTrue("TriggerBack should have been true", triggerBack[0]);
+                    + BackNavigationInfo.typeToString(type), result.mBackNavigationDone);
+            assertTrue("TriggerBack should have been true", result.mTriggerBack);
         }
     }
 
-
-
     @Test
     public void backToHome_dispatchesEvents() throws RemoteException {
         registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
@@ -351,6 +345,65 @@
         verify(mAnimatorCallback, never()).onBackInvoked();
     }
 
+    @Test
+    public void animationNotDefined() throws RemoteException {
+        final int[] testTypes = new int[] {
+                BackNavigationInfo.TYPE_RETURN_TO_HOME,
+                BackNavigationInfo.TYPE_CROSS_TASK,
+                BackNavigationInfo.TYPE_CROSS_ACTIVITY,
+                BackNavigationInfo.TYPE_DIALOG_CLOSE};
+
+        for (int type: testTypes) {
+            final ResultListener result = new ResultListener();
+            createNavigationInfo(new BackNavigationInfo.Builder()
+                    .setType(type)
+                    .setOnBackInvokedCallback(mAppCallback)
+                    .setPrepareRemoteAnimation(true)
+                    .setOnBackNavigationDone(new RemoteCallback(result)));
+            triggerBackGesture();
+            simulateRemoteAnimationStart(type);
+            mShellExecutor.flushAll();
+
+            assertTrue("Navigation Done callback not called for "
+                    + BackNavigationInfo.typeToString(type), result.mBackNavigationDone);
+            assertTrue("TriggerBack should have been true", result.mTriggerBack);
+        }
+
+        verify(mAppCallback, never()).onBackStarted(any());
+        verify(mAppCallback, never()).onBackProgressed(any());
+        verify(mAppCallback, times(testTypes.length)).onBackInvoked();
+
+        verify(mAnimatorCallback, never()).onBackStarted(any());
+        verify(mAnimatorCallback, never()).onBackProgressed(any());
+        verify(mAnimatorCallback, never()).onBackInvoked();
+    }
+
+    @Test
+    public void callbackShouldDeliverProgress() throws RemoteException {
+        registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+
+        final int type = BackNavigationInfo.TYPE_CALLBACK;
+        final ResultListener result = new ResultListener();
+        createNavigationInfo(new BackNavigationInfo.Builder()
+                .setType(type)
+                .setOnBackInvokedCallback(mAppCallback)
+                .setOnBackNavigationDone(new RemoteCallback(result)));
+        triggerBackGesture();
+        mShellExecutor.flushAll();
+
+        assertTrue("Navigation Done callback not called for "
+                + BackNavigationInfo.typeToString(type), result.mBackNavigationDone);
+        assertTrue("TriggerBack should have been true", result.mTriggerBack);
+
+        verify(mAppCallback, times(1)).onBackStarted(any());
+        verify(mAppCallback, times(1)).onBackProgressed(any());
+        verify(mAppCallback, times(1)).onBackInvoked();
+
+        verify(mAnimatorCallback, never()).onBackStarted(any());
+        verify(mAnimatorCallback, never()).onBackProgressed(any());
+        verify(mAnimatorCallback, never()).onBackInvoked();
+    }
+
     private void doMotionEvent(int actionDown, int coordinate) {
         mController.onMotionEvent(
                 coordinate, coordinate,
@@ -377,4 +430,14 @@
         mController.registerAnimation(type,
                 new BackAnimationRunner(mAnimatorCallback, mBackAnimationRunner));
     }
+
+    private static class ResultListener implements RemoteCallback.OnResultListener {
+        boolean mBackNavigationDone = false;
+        boolean mTriggerBack = false;
+        @Override
+        public void onResult(@Nullable Bundle result) {
+            mBackNavigationDone = true;
+            mTriggerBack = result.getBoolean(KEY_TRIGGER_BACK);
+        }
+    };
 }
diff --git a/packages/SystemUI/res/drawable/ic_watch.xml b/packages/SystemUI/res/drawable/ic_watch.xml
new file mode 100644
index 0000000..8ff880c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_watch.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M16,0L8,0l-0.95,5.73C5.19,7.19 4,9.45 4,12s1.19,4.81 3.05,6.27L8,24
+        h8l0.96,-5.73C18.81,16.81 20,14.54 20,12s-1.19,-4.81 -3.04,-6.27L16,0z
+        M12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
+</vector>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 8fa7b11..2b660de 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -21,7 +21,6 @@
 import android.content.res.Resources;
 import android.media.AudioManager;
 import android.os.SystemClock;
-import android.service.trust.TrustAgentService;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.util.MathUtils;
@@ -68,30 +67,24 @@
     private final KeyguardUpdateMonitorCallback mUpdateCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
-                public void onTrustGrantedWithFlags(int flags, int userId, String message) {
-                    if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
-                    boolean bouncerVisible = mView.isVisibleToUser();
-                    boolean temporaryAndRenewable =
-                            (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
-                            != 0;
-                    boolean initiatedByUser =
-                            (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0;
-                    boolean dismissKeyguard =
-                            (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0;
-
-                    if (initiatedByUser || dismissKeyguard) {
-                        if ((mViewMediatorCallback.isScreenOn() || temporaryAndRenewable)
-                                && (bouncerVisible || dismissKeyguard)) {
-                            if (!bouncerVisible) {
-                                // The trust agent dismissed the keyguard without the user proving
-                                // that they are present (by swiping up to show the bouncer). That's
-                                // fine if the user proved presence via some other way to the trust
-                                //agent.
-                                Log.i(TAG, "TrustAgent dismissed Keyguard.");
-                            }
-                            mSecurityCallback.dismiss(false /* authenticated */, userId,
-                                    /* bypassSecondaryLockScreen */ false, SecurityMode.Invalid);
-                        } else {
+                public void onTrustGrantedForCurrentUser(boolean dismissKeyguard,
+                        TrustGrantFlags flags, String message) {
+                    if (dismissKeyguard) {
+                        if (!mView.isVisibleToUser()) {
+                            // The trust agent dismissed the keyguard without the user proving
+                            // that they are present (by swiping up to show the bouncer). That's
+                            // fine if the user proved presence via some other way to the trust
+                            // agent.
+                            Log.i(TAG, "TrustAgent dismissed Keyguard.");
+                        }
+                        mSecurityCallback.dismiss(
+                                false /* authenticated */,
+                                KeyguardUpdateMonitor.getCurrentUser(),
+                                /* bypassSecondaryLockScreen */ false,
+                                SecurityMode.Invalid
+                        );
+                    } else {
+                        if (flags.isInitiatedByUser() || flags.dismissKeyguardRequested()) {
                             mViewMediatorCallback.playTrustedSound();
                         }
                     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d694dc0..2d3dda9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -472,10 +472,12 @@
                     FACE_AUTH_TRIGGERED_TRUST_DISABLED);
         }
 
-        String message = null;
-        if (KeyguardUpdateMonitor.getCurrentUser() == userId) {
-            final boolean userHasTrust = getUserHasTrust(userId);
-            if (userHasTrust && trustGrantedMessages != null) {
+        if (enabled) {
+            String message = null;
+            if (KeyguardUpdateMonitor.getCurrentUser() == userId
+                    && trustGrantedMessages != null) {
+                // Show the first non-empty string provided by a trust agent OR intentionally pass
+                // an empty string through (to prevent the default trust agent string from showing)
                 for (String msg : trustGrantedMessages) {
                     message = msg;
                     if (!TextUtils.isEmpty(message)) {
@@ -483,21 +485,39 @@
                     }
                 }
             }
-        }
-        mLogger.logTrustChanged(wasTrusted, enabled, userId);
-        if (message != null) {
-            mLogger.logShowTrustGrantedMessage(message.toString());
-        }
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-            if (cb != null) {
-                cb.onTrustChanged(userId);
-                if (enabled) {
-                    cb.onTrustGrantedWithFlags(flags, userId, message);
+
+            mLogger.logTrustGrantedWithFlags(flags, userId, message);
+            if (userId == getCurrentUser()) {
+                final TrustGrantFlags trustGrantFlags = new TrustGrantFlags(flags);
+                for (int i = 0; i < mCallbacks.size(); i++) {
+                    KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+                    if (cb != null) {
+                        cb.onTrustGrantedForCurrentUser(
+                                shouldDismissKeyguardOnTrustGrantedWithCurrentUser(trustGrantFlags),
+                                trustGrantFlags, message);
+                    }
                 }
             }
         }
 
+        mLogger.logTrustChanged(wasTrusted, enabled, userId);
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onTrustChanged(userId);
+            }
+        }
+    }
+
+    /**
+     * Whether the trust granted call with its passed flags should dismiss keyguard.
+     * It's assumed that the trust was granted for the current user.
+     */
+    private boolean shouldDismissKeyguardOnTrustGrantedWithCurrentUser(TrustGrantFlags flags) {
+        final boolean isBouncerShowing = mPrimaryBouncerIsOrWillBeShowing || mUdfpsBouncerShowing;
+        return (flags.isInitiatedByUser() || flags.dismissKeyguardRequested())
+                && (mDeviceInteractive || flags.temporaryAndRenewable())
+                && (isBouncerShowing || flags.dismissKeyguardRequested());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index c5142f3..1d58fc9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -19,6 +19,7 @@
 import android.telephony.TelephonyManager;
 import android.view.WindowManagerPolicyConstants;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.settingslib.fuelgauge.BatteryStatus;
@@ -175,11 +176,13 @@
 
     /**
      * Called after trust was granted.
-     * @param userId of the user that has been granted trust
+     * @param dismissKeyguard whether the keyguard should be dismissed as a result of the
+     *                        trustGranted
      * @param message optional message the trust agent has provided to show that should indicate
      *                why trust was granted.
      */
-    public void onTrustGrantedWithFlags(int flags, int userId, @Nullable String message) { }
+    public void onTrustGrantedForCurrentUser(boolean dismissKeyguard,
+            @NonNull TrustGrantFlags flags, @Nullable String message) { }
 
     /**
      * Called when a biometric has been acquired.
diff --git a/packages/SystemUI/src/com/android/keyguard/TrustGrantFlags.java b/packages/SystemUI/src/com/android/keyguard/TrustGrantFlags.java
new file mode 100644
index 0000000..d33732c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/TrustGrantFlags.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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.keyguard;
+
+import android.service.trust.TrustAgentService;
+
+import java.util.Objects;
+
+/**
+ * Translating {@link android.service.trust.TrustAgentService.GrantTrustFlags} to a more
+ * parsable object. These flags are requested by a TrustAgent.
+ */
+public class TrustGrantFlags {
+    final int mFlags;
+
+    public TrustGrantFlags(int flags) {
+        this.mFlags = flags;
+    }
+
+    /** {@link TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER} */
+    public boolean isInitiatedByUser() {
+        return (mFlags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0;
+    }
+
+    /**
+     * Trust agent is requesting to dismiss the keyguard.
+     * See {@link TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD}.
+     *
+     * This does not guarantee that the keyguard is dismissed.
+     * KeyguardUpdateMonitor makes the final determination whether the keyguard should be dismissed.
+     * {@link KeyguardUpdateMonitorCallback#onTrustGrantedForCurrentUser(
+     *      boolean, TrustGrantFlags, String).
+     */
+    public boolean dismissKeyguardRequested() {
+        return (mFlags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0;
+    }
+
+    /** {@link TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE} */
+    public boolean temporaryAndRenewable() {
+        return (mFlags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0;
+    }
+
+    /** {@link TrustAgentService.FLAG_GRANT_TRUST_DISPLAY_MESSAGE} */
+    public boolean displayMessage() {
+        return (mFlags & TrustAgentService.FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof TrustGrantFlags)) {
+            return false;
+        }
+
+        return ((TrustGrantFlags) o).mFlags == this.mFlags;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(mFlags);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        sb.append(mFlags);
+        sb.append("]=");
+
+        if (isInitiatedByUser()) {
+            sb.append("initiatedByUser|");
+        }
+        if (dismissKeyguardRequested()) {
+            sb.append("dismissKeyguard|");
+        }
+        if (temporaryAndRenewable()) {
+            sb.append("temporaryAndRenewable|");
+        }
+        if (displayMessage()) {
+            sb.append("displayMessage|");
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 81b8dfe..6763700 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -25,6 +25,7 @@
 import com.android.keyguard.FaceAuthUiEvent
 import com.android.keyguard.KeyguardListenModel
 import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.keyguard.TrustGrantFlags
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel
 import com.android.systemui.plugins.log.LogLevel.DEBUG
@@ -368,12 +369,16 @@
                 }, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" })
     }
 
-    fun logShowTrustGrantedMessage(
+    fun logTrustGrantedWithFlags(
+            flags: Int,
+            userId: Int,
             message: String?
     ) {
         logBuffer.log(TAG, DEBUG, {
+            int1 = flags
+            int2 = userId
             str1 = message
-        }, { "showTrustGrantedMessage message$str1" })
+        }, { "trustGrantedWithFlags[user=$int2] flags=${TrustGrantFlags(int1)} message=$str1" })
     }
 
     fun logTrustChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 2101efb..8355c64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -67,6 +67,7 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -74,6 +75,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.TrustGrantFlags;
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.settingslib.Utils;
 import com.android.settingslib.fuelgauge.BatteryStatus;
@@ -154,7 +156,8 @@
     private final AccessibilityManager mAccessibilityManager;
     private final Handler mHandler;
 
-    protected KeyguardIndicationRotateTextViewController mRotateTextViewController;
+    @VisibleForTesting
+    public KeyguardIndicationRotateTextViewController mRotateTextViewController;
     private BroadcastReceiver mBroadcastReceiver;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
@@ -1188,9 +1191,9 @@
         }
 
         @Override
-        public void onTrustGrantedWithFlags(int flags, int userId, @Nullable String message) {
-            if (!isCurrentUser(userId)) return;
-            showTrustGrantedMessage(flags, message);
+        public void onTrustGrantedForCurrentUser(boolean dismissKeyguard,
+                @NonNull TrustGrantFlags flags, @Nullable String message) {
+            showTrustGrantedMessage(dismissKeyguard, message);
         }
 
         @Override
@@ -1254,7 +1257,7 @@
         return getCurrentUser() == userId;
     }
 
-    void showTrustGrantedMessage(int flags, @Nullable CharSequence message) {
+    protected void showTrustGrantedMessage(boolean dismissKeyguard, @Nullable String message) {
         mTrustGrantedIndication = message;
         updateDeviceEntryIndication(false);
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 5514fd0..1ec23e9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -29,6 +29,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
 import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
+import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -36,6 +37,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
@@ -89,6 +91,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.dreams.IDreamManager;
+import android.service.trust.TrustAgentService;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -353,7 +356,9 @@
 
     @After
     public void tearDown() {
-        mMockitoSession.finishMocking();
+        if (mMockitoSession != null) {
+            mMockitoSession.finishMocking();
+        }
         cleanupKeyguardUpdateMonitor();
     }
 
@@ -1316,9 +1321,9 @@
                 Arrays.asList("Unlocked by wearable"));
 
         // THEN the showTrustGrantedMessage should be called with the first message
-        verify(mTestCallback).onTrustGrantedWithFlags(
-                eq(0),
-                eq(KeyguardUpdateMonitor.getCurrentUser()),
+        verify(mTestCallback).onTrustGrantedForCurrentUser(
+                anyBoolean(),
+                eq(new TrustGrantFlags(0)),
                 eq("Unlocked by wearable"));
     }
 
@@ -1729,6 +1734,155 @@
         verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
     }
 
+
+    @Test
+    public void testOnTrustGrantedForCurrentUser_dismissKeyguardRequested_deviceInteractive() {
+        // GIVEN device is interactive
+        deviceIsInteractive();
+
+        // GIVEN callback is registered
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN onTrustChanged with TRUST_DISMISS_KEYGUARD flag
+        mKeyguardUpdateMonitor.onTrustChanged(
+                true /* enabled */,
+                getCurrentUser() /* userId */,
+                TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
+                null /* trustGrantedMessages */);
+
+        // THEN onTrustGrantedForCurrentUser callback called
+        verify(callback).onTrustGrantedForCurrentUser(
+                eq(true) /* dismissKeyguard */,
+                eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD)),
+                eq(null) /* message */
+        );
+    }
+
+    @Test
+    public void testOnTrustGrantedForCurrentUser_dismissKeyguardRequested_doesNotDismiss() {
+        // GIVEN device is NOT interactive
+
+        // GIVEN callback is registered
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN onTrustChanged with TRUST_DISMISS_KEYGUARD flag
+        mKeyguardUpdateMonitor.onTrustChanged(
+                true /* enabled */,
+                getCurrentUser() /* userId */,
+                TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
+                null /* trustGrantedMessages */);
+
+        // THEN onTrustGrantedForCurrentUser callback called
+        verify(callback).onTrustGrantedForCurrentUser(
+                eq(false) /* dismissKeyguard */,
+                eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD)),
+                eq(null) /* message */
+        );
+    }
+
+    @Test
+    public void testOnTrustGrantedForCurrentUser_dismissKeyguardRequested_temporaryAndRenewable() {
+        // GIVEN device is interactive
+        deviceIsInteractive();
+
+        // GIVEN callback is registered
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN onTrustChanged for a different user
+        mKeyguardUpdateMonitor.onTrustChanged(
+                true /* enabled */,
+                546 /* userId, not the current userId */,
+                0 /* flags */,
+                null /* trustGrantedMessages */);
+
+        // THEN onTrustGrantedForCurrentUser callback called
+        verify(callback, never()).onTrustGrantedForCurrentUser(
+                anyBoolean() /* dismissKeyguard */,
+                anyObject() /* flags */,
+                anyString() /* message */
+        );
+    }
+
+    @Test
+    public void testOnTrustGranted_differentUser_noCallback() {
+        // GIVEN device is interactive
+
+        // GIVEN callback is registered
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN onTrustChanged with TRUST_DISMISS_KEYGUARD AND TRUST_TEMPORARY_AND_RENEWABLE
+        // flags (temporary & rewable is active unlock)
+        mKeyguardUpdateMonitor.onTrustChanged(
+                true /* enabled */,
+                getCurrentUser() /* userId */,
+                TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
+                        | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */,
+                null /* trustGrantedMessages */);
+
+        // THEN onTrustGrantedForCurrentUser callback called
+        verify(callback).onTrustGrantedForCurrentUser(
+                eq(true) /* dismissKeyguard */,
+                eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
+                        | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)),
+                eq(null) /* message */
+        );
+    }
+
+    @Test
+    public void testOnTrustGrantedForCurrentUser_bouncerShowing_initiatedByUser() {
+        // GIVEN device is interactive & bouncer is showing
+        deviceIsInteractive();
+        bouncerFullyVisible();
+
+        // GIVEN callback is registered
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN onTrustChanged with INITIATED_BY_USER flag
+        mKeyguardUpdateMonitor.onTrustChanged(
+                true /* enabled */,
+                getCurrentUser() /* userId, not the current userId */,
+                TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER /* flags */,
+                null /* trustGrantedMessages */);
+
+        // THEN onTrustGrantedForCurrentUser callback called
+        verify(callback, never()).onTrustGrantedForCurrentUser(
+                eq(true) /* dismissKeyguard */,
+                eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER)),
+                anyString() /* message */
+        );
+    }
+
+    @Test
+    public void testOnTrustGrantedForCurrentUser_bouncerShowing_temporaryRenewable() {
+        // GIVEN device is NOT interactive & bouncer is showing
+        bouncerFullyVisible();
+
+        // GIVEN callback is registered
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN onTrustChanged with INITIATED_BY_USER flag
+        mKeyguardUpdateMonitor.onTrustChanged(
+                true /* enabled */,
+                getCurrentUser() /* userId, not the current userId */,
+                TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER
+                        | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */,
+                null /* trustGrantedMessages */);
+
+        // THEN onTrustGrantedForCurrentUser callback called
+        verify(callback, never()).onTrustGrantedForCurrentUser(
+                eq(true) /* dismissKeyguard */,
+                eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER
+                        | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)),
+                anyString() /* message */
+        );
+    }
+
     private void cleanupKeyguardUpdateMonitor() {
         if (mKeyguardUpdateMonitor != null) {
             mKeyguardUpdateMonitor.removeCallback(mTestCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index e8a7ec8..ab7c52e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -87,6 +87,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.TrustGrantFlags;
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.R;
@@ -1068,7 +1069,8 @@
 
         // GIVEN a trust granted message but trust isn't granted
         final String trustGrantedMsg = "testing trust granted message";
-        mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, trustGrantedMsg);
+        mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
+                false, new TrustGrantFlags(0), trustGrantedMsg);
 
         verifyHideIndication(INDICATION_TYPE_TRUST);
 
@@ -1092,7 +1094,8 @@
 
         // WHEN the showTrustGranted method is called
         final String trustGrantedMsg = "testing trust granted message";
-        mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, trustGrantedMsg);
+        mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
+                false, new TrustGrantFlags(0), trustGrantedMsg);
 
         // THEN verify the trust granted message shows
         verifyIndicationMessage(
@@ -1109,7 +1112,8 @@
         when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
 
         // WHEN the showTrustGranted method is called with a null message
-        mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, null);
+        mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
+                false, new TrustGrantFlags(0), null);
 
         // THEN verify the default trust granted message shows
         verifyIndicationMessage(
@@ -1126,7 +1130,8 @@
         when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
 
         // WHEN the showTrustGranted method is called with an EMPTY string
-        mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, "");
+        mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
+                false, new TrustGrantFlags(0), "");
 
         // THEN verify NO trust message is shown
         verifyNoMessage(INDICATION_TYPE_TRUST);
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index be21075..fbde9e0 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -227,6 +227,12 @@
         return mParams.getName();
     }
 
+    /** Returns the policy specified for this policy type */
+    public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
+            @VirtualDeviceParams.PolicyType int policyType) {
+        return mParams.getDevicePolicy(policyType);
+    }
+
     /** Returns the unique device ID of this device. */
     @Override // Binder call
     public int getDeviceId() {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index c400a74..a8797a0 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -233,6 +233,13 @@
         mLocalService.onAppsOnVirtualDeviceChanged();
     }
 
+    @VisibleForTesting
+    void addVirtualDevice(VirtualDeviceImpl virtualDevice) {
+        synchronized (mVirtualDeviceManagerLock) {
+            mVirtualDevices.put(virtualDevice.getAssociationId(), virtualDevice);
+        }
+    }
+
     class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
             VirtualDeviceImpl.PendingTrampolineCallback {
 
@@ -358,6 +365,12 @@
             return virtualDevices;
         }
 
+        @Override // BinderCall
+        @VirtualDeviceParams.DevicePolicy
+        public int getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType) {
+            return mLocalService.getDevicePolicy(deviceId, policyType);
+        }
+
         @Nullable
         private AssociationInfo getAssociationInfo(String packageName, int associationId) {
             final int callingUserId = getCallingUserHandle().getIdentifier();
@@ -439,6 +452,20 @@
         }
 
         @Override
+        @VirtualDeviceParams.DevicePolicy
+        public int getDevicePolicy(int deviceId, @VirtualDeviceParams.PolicyType int policyType) {
+            synchronized (mVirtualDeviceManagerLock) {
+                for (int i = 0; i < mVirtualDevices.size(); i++) {
+                    final VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
+                    if (device.getDeviceId() == deviceId) {
+                        return device.getDevicePolicy(policyType);
+                    }
+                }
+            }
+            return VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
+        }
+
+        @Override
         public void onVirtualDisplayCreated(int displayId) {
             final VirtualDisplayListener[] listeners;
             synchronized (mVirtualDeviceManagerLock) {
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 81b56a3..d2e572f 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.VirtualDeviceParams;
 
 import java.util.Set;
 
@@ -109,4 +110,14 @@
      * Returns true if the {@code displayId} is owned by any virtual device
      */
     public abstract boolean isDisplayOwnedByAnyVirtualDevice(int displayId);
+
+    /**
+     * Returns the device policy for the given virtual device and policy type.
+     *
+     * <p>In case the virtual device identifier is not valid, or there's no explicitly specified
+     * policy for that device and policy type, then
+     * {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT} is returned.
+     */
+    public abstract @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
+            int deviceId, @VirtualDeviceParams.PolicyType int policyType);
 }
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 4c18d0b..56edde0 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -57,7 +57,6 @@
 import android.view.WindowInsets.Type;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
-import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.Button;
@@ -109,18 +108,13 @@
         mContext = display.getDisplayId() == DEFAULT_DISPLAY
                 ? uiContext : uiContext.createDisplayContext(display);
         mHandler = new H(looper);
-        mShowDelayMs = getNavBarExitDuration() * 3;
+        mShowDelayMs = context.getResources().getInteger(R.integer.dock_enter_exit_duration) * 3L;
         mPanicThresholdMs = context.getResources()
                 .getInteger(R.integer.config_immersive_mode_confirmation_panic);
         mVrModeEnabled = vrModeEnabled;
         mCanSystemBarsBeShownByUser = canSystemBarsBeShownByUser;
     }
 
-    private long getNavBarExitDuration() {
-        Animation exit = AnimationUtils.loadAnimation(mContext, R.anim.dock_bottom_exit);
-        return exit != null ? exit.getDuration() : 0;
-    }
-
     static boolean loadSetting(int currentUserId, Context context) {
         final boolean wasConfirmed = sConfirmed;
         sConfirmed = false;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 5fda3d6..c715a21 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.companion.virtual;
 
+import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
+import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
 import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -44,6 +47,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.companion.AssociationInfo;
 import android.companion.virtual.IVirtualDeviceActivityListener;
+import android.companion.virtual.VirtualDeviceManager;
 import android.companion.virtual.VirtualDeviceParams;
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -240,6 +244,55 @@
                 mAssociationInfo, new Binder(), /* ownerUid */ 0, /* uniqueId */ 1,
                 mInputController, (int associationId) -> {}, mPendingTrampolineCallback,
                 mActivityListener, mRunningAppsChangedCallback, params);
+        mVdms.addVirtualDevice(mDeviceImpl);
+    }
+
+    @Test
+    public void getDevicePolicy_invalidDeviceId_returnsDefault() {
+        assertThat(
+                mLocalService.getDevicePolicy(
+                        VirtualDeviceManager.INVALID_DEVICE_ID, POLICY_TYPE_SENSORS))
+                .isEqualTo(DEVICE_POLICY_DEFAULT);
+    }
+
+    @Test
+    public void getDevicePolicy_defaultDeviceId_returnsDefault() {
+        assertThat(
+                mLocalService.getDevicePolicy(
+                        VirtualDeviceManager.DEFAULT_DEVICE_ID, POLICY_TYPE_SENSORS))
+                .isEqualTo(DEVICE_POLICY_DEFAULT);
+    }
+
+    @Test
+    public void getDevicePolicy_nonExistentDeviceId_returnsDefault() {
+        assertThat(
+                mLocalService.getDevicePolicy(mDeviceImpl.getDeviceId() + 1, POLICY_TYPE_SENSORS))
+                .isEqualTo(DEVICE_POLICY_DEFAULT);
+    }
+
+    @Test
+    public void getDevicePolicy_unspecifiedPolicy_returnsDefault() {
+        assertThat(
+                mLocalService.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS))
+                .isEqualTo(DEVICE_POLICY_DEFAULT);
+    }
+
+    @Test
+    public void getDevicePolicy_returnsCustom() {
+        VirtualDeviceParams params = new VirtualDeviceParams
+                .Builder()
+                .setBlockedActivities(getBlockedActivities())
+                .addDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
+                .build();
+        mDeviceImpl = new VirtualDeviceImpl(mContext,
+                mAssociationInfo, new Binder(), /* ownerUid */ 0, /* uniqueId */ 1,
+                mInputController, (int associationId) -> {}, mPendingTrampolineCallback,
+                mActivityListener, mRunningAppsChangedCallback, params);
+        mVdms.addVirtualDevice(mDeviceImpl);
+
+        assertThat(
+                mLocalService.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS))
+                .isEqualTo(DEVICE_POLICY_CUSTOM);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
index 77f1e24..036b6df 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
@@ -37,6 +37,8 @@
         VirtualDeviceParams originalParams = new VirtualDeviceParams.Builder()
                 .setLockState(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED)
                 .setUsersWithMatchingAccounts(Set.of(UserHandle.of(123), UserHandle.of(456)))
+                .addDevicePolicy(VirtualDeviceParams.POLICY_TYPE_SENSORS,
+                        VirtualDeviceParams.DEVICE_POLICY_CUSTOM)
                 .build();
         Parcel parcel = Parcel.obtain();
         originalParams.writeToParcel(parcel, 0);
@@ -47,5 +49,7 @@
         assertThat(params.getLockState()).isEqualTo(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED);
         assertThat(params.getUsersWithMatchingAccounts())
                 .containsExactly(UserHandle.of(123), UserHandle.of(456));
+        assertThat(params.getDevicePolicy(VirtualDeviceParams.POLICY_TYPE_SENSORS))
+                .isEqualTo(VirtualDeviceParams.DEVICE_POLICY_CUSTOM);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
index 846a506..5e1fae0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
@@ -192,16 +192,17 @@
     }
 
     private static class SyncTarget implements SurfaceSyncGroup.SyncTarget {
-        private SurfaceSyncGroup.SyncBufferCallback mSyncBufferCallback;
+        private SurfaceSyncGroup.TransactionReadyCallback mTransactionReadyCallback;
 
         @Override
-        public void onReadyToSync(SurfaceSyncGroup.SyncBufferCallback syncBufferCallback) {
-            mSyncBufferCallback = syncBufferCallback;
+        public void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
+                SurfaceSyncGroup.TransactionReadyCallback transactionReadyCallback) {
+            mTransactionReadyCallback = transactionReadyCallback;
         }
 
         void onBufferReady() {
             SurfaceControl.Transaction t = new StubTransaction();
-            mSyncBufferCallback.onBufferReady(t);
+            mTransactionReadyCallback.onTransactionReady(t);
         }
     }
 }