Merge "Revert "Basic framework of the UI toolkit variable refresh rate project"" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 90e9c43..c8046ab 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -294,6 +294,12 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+rust_aconfig_library {
+    name: "libandroid_security_flags_rust",
+    crate_name: "android_security_flags",
+    aconfig_declarations: "android.security.flags-aconfig",
+}
+
 // Package Manager
 aconfig_declarations {
     name: "android.content.pm.flags-aconfig",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index d48d84b..1fdf906 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -75,6 +75,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import java.util.regex.Pattern;
 
 /**
  * Maintains the master list of jobs that the job scheduler is tracking. These jobs are compared by
@@ -99,6 +100,8 @@
     private static final long SCHEDULED_JOB_HIGH_WATER_MARK_PERIOD_MS = 30 * 60_000L;
     @VisibleForTesting
     static final String JOB_FILE_SPLIT_PREFIX = "jobs_";
+    private static final Pattern SPLIT_FILE_PATTERN =
+            Pattern.compile("^" + JOB_FILE_SPLIT_PREFIX + "\\d+.xml$");
     private static final int ALL_UIDS = -1;
     @VisibleForTesting
     static final int INVALID_UID = -2;
@@ -1121,6 +1124,11 @@
             int numDuplicates = 0;
             synchronized (mLock) {
                 for (File file : files) {
+                    if (!file.getName().equals("jobs.xml")
+                            && !SPLIT_FILE_PATTERN.matcher(file.getName()).matches()) {
+                        // Skip temporary or other files.
+                        continue;
+                    }
                     final AtomicFile aFile = createJobFile(file);
                     try (FileInputStream fis = aFile.openRead()) {
                         jobs = readJobMapImpl(fis, rtcGood, nowElapsed);
diff --git a/core/api/current.txt b/core/api/current.txt
index 00f0718..7d5417d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9533,6 +9533,7 @@
     method public int getId();
     method public int getSystemDataSyncFlags();
     method @FlaggedApi("android.companion.association_tag") @Nullable public String getTag();
+    method public boolean isSelfManaged();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationInfo> CREATOR;
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f4ddb40..ac61107 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3149,7 +3149,6 @@
 
   public final class AssociationInfo implements android.os.Parcelable {
     method @NonNull public String getPackageName();
-    method public boolean isSelfManaged();
   }
 
   public final class CompanionDeviceManager {
@@ -10483,7 +10482,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isUserOfType(@NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
-    method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public boolean isUserVisible();
+    method public boolean isUserVisible();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int removeUserWhenPossible(@NonNull android.os.UserHandle, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public void setBootUser(@NonNull android.os.UserHandle);
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 06bff5d..b34f678 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -58,11 +58,6 @@
         super(in);
     }
 
-    @Override
-    boolean isActivityLifecycleItem() {
-        return true;
-    }
-
     /** A final lifecycle state that an activity should reach. */
     @LifecycleState
     public abstract int getTargetState();
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index f7f901b..8617386 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -45,13 +45,6 @@
  */
 public class ClientTransaction implements Parcelable, ObjectPoolItem {
 
-    /**
-     * List of transaction items that should be executed in order. Including both
-     * {@link ActivityLifecycleItem} and other {@link ClientTransactionItem}.
-     */
-    @Nullable
-    private List<ClientTransactionItem> mTransactionItems;
-
     /** A list of individual callbacks to a client. */
     @UnsupportedAppUsage
     private List<ClientTransactionItem> mActivityCallbacks;
@@ -71,32 +64,9 @@
     }
 
     /**
-     * Adds a message to the end of the sequence of transaction items.
-     * @param item A single message that can contain a client activity/window request/callback.
-     * TODO(b/260873529): replace both {@link #addCallback} and {@link #setLifecycleStateRequest}.
-     */
-    public void addTransactionItem(@NonNull ClientTransactionItem item) {
-        if (mTransactionItems == null) {
-            mTransactionItems = new ArrayList<>();
-        }
-        mTransactionItems.add(item);
-    }
-
-    /**
-     * Gets the list of client window requests/callbacks.
-     * TODO(b/260873529): must be non null after remove the deprecated methods.
-     */
-    @Nullable
-    public List<ClientTransactionItem> getTransactionItems() {
-        return mTransactionItems;
-    }
-
-    /**
-     * Adds a message to the end of the sequence of callbacks.
+     * Add a message to the end of the sequence of callbacks.
      * @param activityCallback A single message that can contain a lifecycle request/callback.
-     * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead.
      */
-    @Deprecated
     public void addCallback(@NonNull ClientTransactionItem activityCallback) {
         if (mActivityCallbacks == null) {
             mActivityCallbacks = new ArrayList<>();
@@ -104,35 +74,25 @@
         mActivityCallbacks.add(activityCallback);
     }
 
-    /**
-     * Gets the list of callbacks.
-     * @deprecated use {@link #getTransactionItems()} instead.
-     */
+    /** Get the list of callbacks. */
     @Nullable
     @VisibleForTesting
     @UnsupportedAppUsage
-    @Deprecated
     public List<ClientTransactionItem> getCallbacks() {
         return mActivityCallbacks;
     }
 
-    /**
-     * Gets the target state lifecycle request.
-     * @deprecated use {@link #getTransactionItems()} instead.
-     */
+    /** Get the target state lifecycle request. */
     @VisibleForTesting(visibility = PACKAGE)
     @UnsupportedAppUsage
-    @Deprecated
     public ActivityLifecycleItem getLifecycleStateRequest() {
         return mLifecycleStateRequest;
     }
 
     /**
-     * Sets the lifecycle state in which the client should be after executing the transaction.
+     * Set the lifecycle state in which the client should be after executing the transaction.
      * @param stateRequest A lifecycle request initialized with right parameters.
-     * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead.
      */
-    @Deprecated
     public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
         mLifecycleStateRequest = stateRequest;
     }
@@ -143,14 +103,6 @@
      *                                 requested by transaction items.
      */
     public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) {
-        if (mTransactionItems != null) {
-            final int size = mTransactionItems.size();
-            for (int i = 0; i < size; ++i) {
-                mTransactionItems.get(i).preExecute(clientTransactionHandler);
-            }
-            return;
-        }
-
         if (mActivityCallbacks != null) {
             final int size = mActivityCallbacks.size();
             for (int i = 0; i < size; ++i) {
@@ -195,19 +147,12 @@
 
     @Override
     public void recycle() {
-        if (mTransactionItems != null) {
-            int size = mTransactionItems.size();
-            for (int i = 0; i < size; i++) {
-                mTransactionItems.get(i).recycle();
-            }
-            mTransactionItems = null;
-        }
         if (mActivityCallbacks != null) {
             int size = mActivityCallbacks.size();
             for (int i = 0; i < size; i++) {
                 mActivityCallbacks.get(i).recycle();
             }
-            mActivityCallbacks = null;
+            mActivityCallbacks.clear();
         }
         if (mLifecycleStateRequest != null) {
             mLifecycleStateRequest.recycle();
@@ -220,15 +165,8 @@
     // Parcelable implementation
 
     /** Write to Parcel. */
-    @SuppressWarnings("AndroidFrameworkEfficientParcelable") // Item class is not final.
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        final boolean writeTransactionItems = mTransactionItems != null;
-        dest.writeBoolean(writeTransactionItems);
-        if (writeTransactionItems) {
-            dest.writeParcelableList(mTransactionItems, flags);
-        }
-
         dest.writeParcelable(mLifecycleStateRequest, flags);
         final boolean writeActivityCallbacks = mActivityCallbacks != null;
         dest.writeBoolean(writeActivityCallbacks);
@@ -239,20 +177,11 @@
 
     /** Read from Parcel. */
     private ClientTransaction(@NonNull Parcel in) {
-        final boolean readTransactionItems = in.readBoolean();
-        if (readTransactionItems) {
-            mTransactionItems = new ArrayList<>();
-            in.readParcelableList(mTransactionItems, getClass().getClassLoader(),
-                    ClientTransactionItem.class);
-        }
-
-        mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(),
-                ActivityLifecycleItem.class);
+        mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), android.app.servertransaction.ActivityLifecycleItem.class);
         final boolean readActivityCallbacks = in.readBoolean();
         if (readActivityCallbacks) {
             mActivityCallbacks = new ArrayList<>();
-            in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(),
-                    ClientTransactionItem.class);
+            in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(), android.app.servertransaction.ClientTransactionItem.class);
         }
     }
 
@@ -280,8 +209,7 @@
             return false;
         }
         final ClientTransaction other = (ClientTransaction) o;
-        return Objects.equals(mTransactionItems, other.mTransactionItems)
-                && Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
+        return Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
                 && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest)
                 && mClient == other.mClient;
     }
@@ -289,7 +217,6 @@
     @Override
     public int hashCode() {
         int result = 17;
-        result = 31 * result + Objects.hashCode(mTransactionItems);
         result = 31 * result + Objects.hashCode(mActivityCallbacks);
         result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
         result = 31 * result + Objects.hashCode(mClient);
@@ -300,22 +227,6 @@
     void dump(@NonNull String prefix, @NonNull PrintWriter pw,
             @NonNull ClientTransactionHandler transactionHandler) {
         pw.append(prefix).println("ClientTransaction{");
-        if (mTransactionItems != null) {
-            pw.append(prefix).print("  transactionItems=[");
-            final String itemPrefix = prefix + "    ";
-            final int size = mTransactionItems.size();
-            if (size > 0) {
-                pw.println();
-                for (int i = 0; i < size; i++) {
-                    mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler);
-                }
-                pw.append(prefix).println("  ]");
-            } else {
-                pw.println("]");
-            }
-            pw.append(prefix).println("}");
-            return;
-        }
         pw.append(prefix).print("  callbacks=[");
         final String itemPrefix = prefix + "    ";
         final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
index f94e22d..07e5a7d 100644
--- a/core/java/android/app/servertransaction/ClientTransactionItem.java
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -72,13 +72,6 @@
         return null;
     }
 
-    /**
-     * Whether this is a {@link ActivityLifecycleItem}.
-     */
-    boolean isActivityLifecycleItem() {
-        return false;
-    }
-
     /** Dumps this transaction item. */
     void dump(@NonNull String prefix, @NonNull PrintWriter pw,
             @NonNull ClientTransactionHandler transactionHandler) {
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 9f5e0dc..066f9fe 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -28,7 +28,6 @@
 import static android.app.servertransaction.TransactionExecutorHelper.getShortActivityName;
 import static android.app.servertransaction.TransactionExecutorHelper.getStateName;
 import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
-import static android.app.servertransaction.TransactionExecutorHelper.shouldExcludeLastLifecycleState;
 import static android.app.servertransaction.TransactionExecutorHelper.tId;
 import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;
 
@@ -62,9 +61,6 @@
     private final PendingTransactionActions mPendingActions = new PendingTransactionActions();
     private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
 
-    /** Keeps track of display ids whose Configuration got updated within a transaction. */
-    private final ArraySet<Integer> mConfigUpdatedDisplayIds = new ArraySet<>();
-
     /** Initialize an instance with transaction handler, that will execute all requested actions. */
     public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) {
         mTransactionHandler = clientTransactionHandler;
@@ -83,52 +79,15 @@
             Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
         }
 
-        if (transaction.getTransactionItems() != null) {
-            executeTransactionItems(transaction);
-        } else {
-            // TODO(b/260873529): cleanup after launch.
-            executeCallbacks(transaction);
-            executeLifecycleState(transaction);
-        }
-
-        if (!mConfigUpdatedDisplayIds.isEmpty()) {
-            // Whether this transaction should trigger DisplayListener#onDisplayChanged.
-            final ClientTransactionListenerController controller =
-                    ClientTransactionListenerController.getInstance();
-            final int displayCount = mConfigUpdatedDisplayIds.size();
-            for (int i = 0; i < displayCount; i++) {
-                final int displayId = mConfigUpdatedDisplayIds.valueAt(i);
-                controller.onDisplayChanged(displayId);
-            }
-            mConfigUpdatedDisplayIds.clear();
-        }
+        executeCallbacks(transaction);
+        executeLifecycleState(transaction);
 
         mPendingActions.clear();
         if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
     }
 
-    /** Cycles through all transaction items and execute them at proper times. */
+    /** Cycle through all states requested by callbacks and execute them at proper times. */
     @VisibleForTesting
-    public void executeTransactionItems(@NonNull ClientTransaction transaction) {
-        final List<ClientTransactionItem> items = transaction.getTransactionItems();
-        final int size = items.size();
-        for (int i = 0; i < size; i++) {
-            final ClientTransactionItem item = items.get(i);
-            if (item.isActivityLifecycleItem()) {
-                executeLifecycleItem(transaction, (ActivityLifecycleItem) item);
-            } else {
-                executeNonLifecycleItem(transaction, item,
-                        shouldExcludeLastLifecycleState(items, i));
-            }
-        }
-    }
-
-    /**
-     * Cycle through all states requested by callbacks and execute them at proper times.
-     * @deprecated use {@link #executeTransactionItems} instead.
-     */
-    @VisibleForTesting
-    @Deprecated
     public void executeCallbacks(@NonNull ClientTransaction transaction) {
         final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
         if (callbacks == null || callbacks.isEmpty()) {
@@ -146,78 +105,83 @@
         // Index of the last callback that requests some post-execution state.
         final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);
 
+        // Keep track of display ids whose Configuration got updated with this transaction.
+        ArraySet<Integer> configUpdatedDisplays = null;
+
         final int size = callbacks.size();
         for (int i = 0; i < size; ++i) {
             final ClientTransactionItem item = callbacks.get(i);
+            final IBinder token = item.getActivityToken();
+            ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
 
-            // Skip the very last transition and perform it by explicit state request instead.
+            if (token != null && r == null
+                    && mTransactionHandler.getActivitiesToBeDestroyed().containsKey(token)) {
+                // The activity has not been created but has been requested to destroy, so all
+                // transactions for the token are just like being cancelled.
+                Slog.w(TAG, "Skip pre-destroyed transaction item:\n" + item);
+                continue;
+            }
+
+            if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
             final int postExecutionState = item.getPostExecutionState();
-            final boolean shouldExcludeLastLifecycleState = postExecutionState != UNDEFINED
-                    && i == lastCallbackRequestingState && finalState == postExecutionState;
-            executeNonLifecycleItem(transaction, item, shouldExcludeLastLifecycleState);
-        }
-    }
 
-    private void executeNonLifecycleItem(@NonNull ClientTransaction transaction,
-            @NonNull ClientTransactionItem item, boolean shouldExcludeLastLifecycleState) {
-        final IBinder token = item.getActivityToken();
-        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+            if (item.shouldHaveDefinedPreExecutionState()) {
+                final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
+                        item.getPostExecutionState());
+                if (closestPreExecutionState != UNDEFINED) {
+                    cycleToPath(r, closestPreExecutionState, transaction);
+                }
+            }
 
-        if (token != null && r == null
-                && mTransactionHandler.getActivitiesToBeDestroyed().containsKey(token)) {
-            // The activity has not been created but has been requested to destroy, so all
-            // transactions for the token are just like being cancelled.
-            Slog.w(TAG, "Skip pre-destroyed transaction item:\n" + item);
-            return;
-        }
+            // Can't read flag from isolated process.
+            final boolean isSyncWindowConfigUpdateFlagEnabled = !Process.isIsolated()
+                    && syncWindowConfigUpdateFlag();
+            final Context configUpdatedContext = isSyncWindowConfigUpdateFlagEnabled
+                    ? item.getContextToUpdate(mTransactionHandler)
+                    : null;
+            final Configuration preExecutedConfig = configUpdatedContext != null
+                    ? new Configuration(configUpdatedContext.getResources().getConfiguration())
+                    : null;
 
-        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
-        final int postExecutionState = item.getPostExecutionState();
+            item.execute(mTransactionHandler, mPendingActions);
 
-        if (item.shouldHaveDefinedPreExecutionState()) {
-            final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
-                    postExecutionState);
-            if (closestPreExecutionState != UNDEFINED) {
-                cycleToPath(r, closestPreExecutionState, transaction);
+            if (configUpdatedContext != null) {
+                final Configuration postExecutedConfig = configUpdatedContext.getResources()
+                        .getConfiguration();
+                if (!areConfigurationsEqualForDisplay(postExecutedConfig, preExecutedConfig)) {
+                    if (configUpdatedDisplays == null) {
+                        configUpdatedDisplays = new ArraySet<>();
+                    }
+                    configUpdatedDisplays.add(configUpdatedContext.getDisplayId());
+                }
+            }
+
+            item.postExecute(mTransactionHandler, mPendingActions);
+            if (r == null) {
+                // Launch activity request will create an activity record.
+                r = mTransactionHandler.getActivityClient(token);
+            }
+
+            if (postExecutionState != UNDEFINED && r != null) {
+                // Skip the very last transition and perform it by explicit state request instead.
+                final boolean shouldExcludeLastTransition =
+                        i == lastCallbackRequestingState && finalState == postExecutionState;
+                cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
             }
         }
 
-        // Can't read flag from isolated process.
-        final boolean isSyncWindowConfigUpdateFlagEnabled = !Process.isIsolated()
-                && syncWindowConfigUpdateFlag();
-        final Context configUpdatedContext = isSyncWindowConfigUpdateFlagEnabled
-                ? item.getContextToUpdate(mTransactionHandler)
-                : null;
-        final Configuration preExecutedConfig = configUpdatedContext != null
-                ? new Configuration(configUpdatedContext.getResources().getConfiguration())
-                : null;
-
-        item.execute(mTransactionHandler, mPendingActions);
-
-        if (configUpdatedContext != null) {
-            final Configuration postExecutedConfig = configUpdatedContext.getResources()
-                    .getConfiguration();
-            if (!areConfigurationsEqualForDisplay(postExecutedConfig, preExecutedConfig)) {
-                mConfigUpdatedDisplayIds.add(configUpdatedContext.getDisplayId());
+        if (configUpdatedDisplays != null) {
+            final ClientTransactionListenerController controller =
+                    ClientTransactionListenerController.getInstance();
+            final int displayCount = configUpdatedDisplays.size();
+            for (int i = 0; i < displayCount; i++) {
+                final int displayId = configUpdatedDisplays.valueAt(i);
+                controller.onDisplayChanged(displayId);
             }
         }
-
-        item.postExecute(mTransactionHandler, mPendingActions);
-        if (r == null) {
-            // Launch activity request will create an activity record.
-            r = mTransactionHandler.getActivityClient(token);
-        }
-
-        if (postExecutionState != UNDEFINED && r != null) {
-            cycleToPath(r, postExecutionState, shouldExcludeLastLifecycleState, transaction);
-        }
     }
 
-    /**
-     * Transition to the final state if requested by the transaction.
-     * @deprecated use {@link #executeTransactionItems} instead
-     */
-    @Deprecated
+    /** Transition to the final state if requested by the transaction. */
     private void executeLifecycleState(@NonNull ClientTransaction transaction) {
         final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
         if (lifecycleItem == null) {
@@ -225,11 +189,6 @@
             return;
         }
 
-        executeLifecycleItem(transaction, lifecycleItem);
-    }
-
-    private void executeLifecycleItem(@NonNull ClientTransaction transaction,
-            @NonNull ActivityLifecycleItem lifecycleItem) {
         final IBinder token = lifecycleItem.getActivityToken();
         final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
         if (DEBUG_RESOLVER) {
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index dfbccb4..7e89a5b 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -236,39 +236,21 @@
      * index 1 will be returned, because ActivityResult request on position 1 will be the last
      * request that moves activity to the RESUMED state where it will eventually end.
      */
-    static int lastCallbackRequestingState(@NonNull ClientTransaction transaction) {
+    static int lastCallbackRequestingState(ClientTransaction transaction) {
         final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
-        if (callbacks == null || callbacks.isEmpty()
-                || transaction.getLifecycleStateRequest() == null) {
+        if (callbacks == null || callbacks.size() == 0) {
             return -1;
         }
-        return lastCallbackRequestingStateIndex(callbacks, 0, callbacks.size() - 1,
-                transaction.getLifecycleStateRequest().getActivityToken());
-    }
 
-    /**
-     * Returns the index of the last callback between the start index and last index that requests
-     * the state for the given activity token in which that activity will be after execution.
-     * If there is a group of callbacks in the end that requests the same specific state or doesn't
-     * request any - we will find the first one from such group.
-     *
-     * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any
-     * specific state. If there is a sequence
-     *   Configuration - ActivityResult - Configuration - ActivityResult
-     * index 1 will be returned, because ActivityResult request on position 1 will be the last
-     * request that moves activity to the RESUMED state where it will eventually end.
-     */
-    private static int lastCallbackRequestingStateIndex(@NonNull List<ClientTransactionItem> items,
-            int startIndex, int lastIndex, @NonNull IBinder activityToken) {
         // Go from the back of the list to front, look for the request closes to the beginning that
         // requests the state in which activity will end after all callbacks are executed.
         int lastRequestedState = UNDEFINED;
         int lastRequestingCallback = -1;
-        for (int i = lastIndex; i >= startIndex; i--) {
-            final ClientTransactionItem item = items.get(i);
-            final int postExecutionState = item.getPostExecutionState();
-            if (postExecutionState != UNDEFINED && activityToken.equals(item.getActivityToken())) {
-                // Found a callback that requests some post-execution state for the given activity.
+        for (int i = callbacks.size() - 1; i >= 0; i--) {
+            final ClientTransactionItem callback = callbacks.get(i);
+            final int postExecutionState = callback.getPostExecutionState();
+            if (postExecutionState != UNDEFINED) {
+                // Found a callback that requests some post-execution state.
                 if (lastRequestedState == UNDEFINED || lastRequestedState == postExecutionState) {
                     // It's either a first-from-end callback that requests state or it requests
                     // the same state as the last one. In both cases, we will use it as the new
@@ -284,53 +266,6 @@
         return lastRequestingCallback;
     }
 
-    /**
-     * For the transaction item at {@code currentIndex}, if it is requesting post execution state,
-     * whether or not to exclude the last state. This only returns {@code true} when there is a
-     * following explicit {@link ActivityLifecycleItem} requesting the same state for the same
-     * activity, so that last state will be covered by the following {@link ActivityLifecycleItem}.
-     */
-    static boolean shouldExcludeLastLifecycleState(@NonNull List<ClientTransactionItem> items,
-            int currentIndex) {
-        final ClientTransactionItem item = items.get(currentIndex);
-        final IBinder activityToken = item.getActivityToken();
-        final int postExecutionState = item.getPostExecutionState();
-        if (activityToken == null || postExecutionState == UNDEFINED) {
-            // Not a transaction item requesting post execution state.
-            return false;
-        }
-        final int nextLifecycleItemIndex = findNextLifecycleItemIndex(items, currentIndex + 1,
-                activityToken);
-        if (nextLifecycleItemIndex == -1) {
-            // No following ActivityLifecycleItem for this activity token.
-            return false;
-        }
-        final ActivityLifecycleItem lifecycleItem =
-                (ActivityLifecycleItem) items.get(nextLifecycleItemIndex);
-        if (postExecutionState != lifecycleItem.getTargetState()) {
-            // The explicit ActivityLifecycleItem is not requesting the same state.
-            return false;
-        }
-        // Only exclude for the first non-lifecycle item that requests the same specific state.
-        return currentIndex == lastCallbackRequestingStateIndex(items, currentIndex,
-                nextLifecycleItemIndex - 1, activityToken);
-    }
-
-    /**
-     * Finds the index of the next {@link ActivityLifecycleItem} for the given activity token.
-     */
-    private static int findNextLifecycleItemIndex(@NonNull List<ClientTransactionItem> items,
-            int startIndex, @NonNull IBinder activityToken) {
-        final int size = items.size();
-        for (int i = startIndex; i < size; i++) {
-            final ClientTransactionItem item = items.get(i);
-            if (item.isActivityLifecycleItem() && item.getActivityToken().equals(activityToken)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
     /** Dump transaction to string. */
     static String transactionToString(@NonNull ClientTransaction transaction,
             @NonNull ClientTransactionHandler transactionHandler) {
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 8b09bdf..161fa79 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -206,9 +206,8 @@
     /**
      * @return whether the association is managed by the companion application it belongs to.
      * @see AssociationRequest.Builder#setSelfManaged(boolean)
-     * @hide
      */
-    @SystemApi
+    @SuppressLint("UnflaggedApi") // promoting from @SystemApi
     public boolean isSelfManaged() {
         return mSelfManaged;
     }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 4c8ef97..9034ff1 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3330,7 +3330,10 @@
      *
      * @return whether the context user is running in the foreground.
      */
-    @UserHandleAware
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
     public boolean isUserForeground() {
         try {
             return mService.isUserForeground(mUserId);
@@ -3404,11 +3407,10 @@
      * @hide
      */
     @SystemApi
-    @UserHandleAware
-    @RequiresPermission(anyOf = {
-            "android.permission.INTERACT_ACROSS_USERS",
-            "android.permission.MANAGE_USERS"
-    })
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
     public boolean isUserVisible() {
         try {
             return mService.isUserVisible(mUserId);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 17c82b6..a0628c4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9854,7 +9854,10 @@
                 outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
                 outAttrs.setInitialSurroundingText(mText);
                 outAttrs.contentMimeTypes = getReceiveContentMimeTypes();
-
+                if (android.view.inputmethod.Flags.editorinfoHandwritingEnabled()
+                        && isAutoHandwritingEnabled()) {
+                    outAttrs.setStylusHandwritingEnabled(true);
+                }
                 ArrayList<Class<? extends HandwritingGesture>> gestures = new ArrayList<>();
                 gestures.add(SelectGesture.class);
                 gestures.add(SelectRangeGesture.class);
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index b1d22e0..77e1502 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -81,11 +81,6 @@
         public static final Flag PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS = releasedFlag(
                 "persist.sysui.notification.propagate_channel_updates_to_conversations");
 
-        // TODO: b/291907312 - remove feature flags
-        /** Gating the NMS->NotificationAttentionHelper buzzBeepBlink refactor */
-        public static final Flag ENABLE_ATTENTION_HELPER_REFACTOR = devFlag(
-                "persist.debug.sysui.notification.enable_attention_helper_refactor");
-
         // TODO b/291899544: for released flags, use resource config values
         /** Value used by polite notif. feature */
         public static final Flag NOTIF_COOLDOWN_T1 = devFlag(
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index d10cf16..531404b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -62,24 +62,4 @@
         verify(callback2, times(1)).preExecute(clientTransactionHandler);
         verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
     }
-
-    @Test
-    public void testPreExecuteTransactionItems() {
-        final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
-        final ClientTransactionHandler clientTransactionHandler =
-                mock(ClientTransactionHandler.class);
-
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addTransactionItem(callback1);
-        transaction.addTransactionItem(callback2);
-        transaction.addTransactionItem(stateRequest);
-
-        transaction.preExecute(clientTransactionHandler);
-
-        verify(callback1, times(1)).preExecute(clientTransactionHandler);
-        verify(callback2, times(1)).preExecute(clientTransactionHandler);
-        verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
-    }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index f2b0f2e..44a4d58 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -247,31 +247,6 @@
     }
 
     @Test
-    public void testExecuteTransactionItems_transactionResolution() {
-        ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
-        ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
-        ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
-        IBinder token = mock(IBinder.class);
-        when(stateRequest.getActivityToken()).thenReturn(token);
-        when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
-
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addTransactionItem(callback1);
-        transaction.addTransactionItem(callback2);
-        transaction.addTransactionItem(stateRequest);
-
-        transaction.preExecute(mTransactionHandler);
-        mExecutor.execute(transaction);
-
-        InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
-        inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
-        inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
-        inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
-    }
-
-    @Test
     public void testDoNotLaunchDestroyedActivity() {
         final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
         when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
@@ -304,43 +279,12 @@
     }
 
     @Test
-    public void testExecuteTransactionItems_doNotLaunchDestroyedActivity() {
-        final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
-        when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
-        // Assume launch transaction is still in queue, so there is no client record.
-        when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
-
-        // An incoming destroy transaction enters binder thread (preExecute).
-        final IBinder token = mock(IBinder.class);
-        final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
-        destroyTransaction.addTransactionItem(
-                DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
-        destroyTransaction.preExecute(mTransactionHandler);
-        // The activity should be added to to-be-destroyed container.
-        assertEquals(1, activitiesToBeDestroyed.size());
-
-        // A previous queued launch transaction runs on main thread (execute).
-        final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
-        final LaunchActivityItem launchItem =
-                spy(new LaunchActivityItemBuilder().setActivityToken(token).build());
-        launchTransaction.addTransactionItem(launchItem);
-        mExecutor.execute(launchTransaction);
-
-        // The launch transaction should not be executed because its token is in the
-        // to-be-destroyed container.
-        verify(launchItem, never()).execute(any(), any());
-
-        // After the destroy transaction has been executed, the token should be removed.
-        mExecutor.execute(destroyTransaction);
-        assertTrue(activitiesToBeDestroyed.isEmpty());
-    }
-
-    @Test
     public void testActivityResultRequiredStateResolution() {
         when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
 
         PostExecItem postExecItem = new PostExecItem(ON_RESUME);
 
+        IBinder token = mock(IBinder.class);
         ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.addCallback(postExecItem);
 
@@ -356,26 +300,6 @@
     }
 
     @Test
-    public void testExecuteTransactionItems_activityResultRequiredStateResolution() {
-        when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
-
-        PostExecItem postExecItem = new PostExecItem(ON_RESUME);
-
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addTransactionItem(postExecItem);
-
-        // Verify resolution that should get to onPause
-        mClientRecord.setState(ON_RESUME);
-        mExecutor.executeTransactionItems(transaction);
-        verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
-
-        // Verify resolution that should get to onStart
-        mClientRecord.setState(ON_STOP);
-        mExecutor.executeTransactionItems(transaction);
-        verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
-    }
-
-    @Test
     public void testClosestStateResolutionForSameState() {
         final int[] allStates = new int[] {
                 ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY};
@@ -520,18 +444,6 @@
         mExecutor.executeCallbacks(transaction);
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void testExecuteTransactionItems_activityItemNullRecordThrowsException() {
-        final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
-        when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
-        final IBinder token = mock(IBinder.class);
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addTransactionItem(activityItem);
-        when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
-
-        mExecutor.executeTransactionItems(transaction);
-    }
-
     @Test
     public void testActivityItemExecute() {
         final IBinder token = mock(IBinder.class);
@@ -552,26 +464,6 @@
         inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
     }
 
-    @Test
-    public void testExecuteTransactionItems_activityItemExecute() {
-        final IBinder token = mock(IBinder.class);
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
-        when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
-        when(activityItem.getActivityToken()).thenReturn(token);
-        transaction.addTransactionItem(activityItem);
-        final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
-        transaction.addTransactionItem(stateRequest);
-        when(stateRequest.getActivityToken()).thenReturn(token);
-        when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
-
-        mExecutor.execute(transaction);
-
-        final InOrder inOrder = inOrder(activityItem, stateRequest);
-        inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any());
-        inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
-    }
-
     private static int[] shuffledArray(int[] inputArray) {
         final List<Integer> list = Arrays.stream(inputArray).boxed().collect(Collectors.toList());
         Collections.shuffle(list);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 4aa62c5..7d047c9 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -285,10 +285,6 @@
                 78 /* configChanges */);
 
         ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addTransactionItem(callback1);
-        transaction.addTransactionItem(callback2);
-        transaction.addTransactionItem(lifecycleRequest);
-
         transaction.addCallback(callback1);
         transaction.addCallback(callback2);
         transaction.setLifecycleStateRequest(lifecycleRequest);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b71aaf3..cc73ece 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2887,6 +2887,12 @@
       "group": "WM_DEBUG_RESIZE",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "419378610": {
+      "message": "Content Recording: Apply transformations of shift %d x %d, scale %f x %f, crop (aka recorded content size) %d x %d for display %d; display has size %d x %d; surface has size %d x %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/ContentRecorder.java"
+    },
     "422634333": {
       "message": "First draw done in potential wallpaper target %s",
       "level": "VERBOSE",
@@ -4339,12 +4345,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "1936800105": {
-      "message": "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka recorded content size) %d x %d for display %d; display has size %d x %d; surface has size %d x %d",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_CONTENT_RECORDING",
-      "at": "com\/android\/server\/wm\/ContentRecorder.java"
-    },
     "1945495497": {
       "message": "Focused window didn't have a valid surface drawn.",
       "level": "DEBUG",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 738c94e..79f306e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -19,6 +19,7 @@
 import static android.view.View.LAYOUT_DIRECTION_RTL;
 
 import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.bubbles.animation.FlingToDismissUtils.getFlingToDismissTargetWidth;
 
 import android.content.res.Resources;
 import android.graphics.Path;
@@ -375,6 +376,9 @@
         mMagnetizedBubbleDraggingOut.setMagnetListener(listener);
         mMagnetizedBubbleDraggingOut.setHapticsEnabled(true);
         mMagnetizedBubbleDraggingOut.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
+        int screenWidthPx = mLayout.getContext().getResources().getDisplayMetrics().widthPixels;
+        mMagnetizedBubbleDraggingOut.setFlingToTargetWidthPercent(
+                getFlingToDismissTargetWidth(screenWidthPx));
     }
 
     private void springBubbleTo(View bubble, float x, float y) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/FlingToDismissUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/FlingToDismissUtils.kt
new file mode 100644
index 0000000..2a44f04
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/FlingToDismissUtils.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles.animation
+
+/** Utils related to the fling to dismiss animation. */
+object FlingToDismissUtils {
+
+    /** The target width surrounding the dismiss target on a small width screen, e.g. phone. */
+    private const val FLING_TO_DISMISS_TARGET_WIDTH_SMALL = 3f
+    /**
+     * The target width surrounding the dismiss target on a medium width screen, e.g. tablet in
+     * portrait.
+     */
+    private const val FLING_TO_DISMISS_TARGET_WIDTH_MEDIUM = 4.5f
+    /**
+     * The target width surrounding the dismiss target on a large width screen, e.g. tablet in
+     * landscape.
+     */
+    private const val FLING_TO_DISMISS_TARGET_WIDTH_LARGE = 6f
+
+    /** Returns the dismiss target width for the specified [screenWidthPx]. */
+    @JvmStatic
+    fun getFlingToDismissTargetWidth(screenWidthPx: Int) = when {
+        screenWidthPx >= 2000 -> FLING_TO_DISMISS_TARGET_WIDTH_LARGE
+        screenWidthPx >= 1500 -> FLING_TO_DISMISS_TARGET_WIDTH_MEDIUM
+        else -> FLING_TO_DISMISS_TARGET_WIDTH_SMALL
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index aad2683..e487328 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.bubbles.animation;
 
 import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.bubbles.animation.FlingToDismissUtils.getFlingToDismissTargetWidth;
 
 import android.content.ContentResolver;
 import android.content.res.Resources;
@@ -851,6 +852,15 @@
         if (mLayout != null) {
             Resources res = mLayout.getContext().getResources();
             mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
+            updateFlingToDismissTargetWidth();
+        }
+    }
+
+    private void updateFlingToDismissTargetWidth() {
+        if (mLayout != null && mMagnetizedStack != null) {
+            int screenWidthPx = mLayout.getResources().getDisplayMetrics().widthPixels;
+            mMagnetizedStack.setFlingToTargetWidthPercent(
+                    getFlingToDismissTargetWidth(screenWidthPx));
         }
     }
 
@@ -1022,23 +1032,8 @@
             };
             mMagnetizedStack.setHapticsEnabled(true);
             mMagnetizedStack.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
+            updateFlingToDismissTargetWidth();
         }
-
-        final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
-        final float minVelocity = Settings.Secure.getFloat(contentResolver,
-                "bubble_dismiss_fling_min_velocity",
-                mMagnetizedStack.getFlingToTargetMinVelocity() /* default */);
-        final float maxVelocity = Settings.Secure.getFloat(contentResolver,
-                "bubble_dismiss_stick_max_velocity",
-                mMagnetizedStack.getStickToTargetMaxXVelocity() /* default */);
-        final float targetWidth = Settings.Secure.getFloat(contentResolver,
-                "bubble_dismiss_target_width_percent",
-                mMagnetizedStack.getFlingToTargetWidthPercent() /* default */);
-
-        mMagnetizedStack.setFlingToTargetMinVelocity(minVelocity);
-        mMagnetizedStack.setStickToTargetMaxXVelocity(maxVelocity);
-        mMagnetizedStack.setFlingToTargetWidthPercent(targetWidth);
-
         return mMagnetizedStack;
     }
 
@@ -1053,7 +1048,7 @@
      * property directly to move the first bubble and cause the stack to 'follow' to the new
      * location.
      *
-     * This could also be achieved by simply animating the first bubble view and adding an update
+     * <p>This could also be achieved by simply animating the first bubble view and adding an update
      * listener to dispatch movement to the rest of the stack. However, this would require
      * duplication of logic in that update handler - it's simpler to keep all logic contained in the
      * {@link #moveFirstBubbleWithStackFollowing} method.
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 6c4b695..af35ea4 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -68,6 +68,7 @@
     override var launchContainer = ghostedView.rootView as ViewGroup
     private val launchContainerOverlay: ViewGroupOverlay
         get() = launchContainer.overlay
+
     private val launchContainerLocation = IntArray(2)
 
     /** The ghost view that is drawn and animated instead of the ghosted view. */
@@ -206,9 +207,8 @@
             return
         }
 
-        backgroundView = FrameLayout(launchContainer.context).also {
-            launchContainerOverlay.add(it)
-        }
+        backgroundView =
+            FrameLayout(launchContainer.context).also { launchContainerOverlay.add(it) }
 
         // We wrap the ghosted view background and use it to draw the expandable background. Its
         // alpha will be set to 0 as soon as we start drawing the expanding background.
@@ -226,6 +226,17 @@
         // the content before fading out the background.
         ghostView = GhostView.addGhost(ghostedView, launchContainer)
 
+        // [GhostView.addGhost], the result of which is our [ghostView], creates a [GhostView], and
+        // adds it first to a [FrameLayout] container. It then adds _that_ container to an
+        // [OverlayViewGroup]. We need to turn off clipping for that container view. Currently,
+        // however, the only way to get a reference to that overlay is by going through our
+        // [ghostView]. The [OverlayViewGroup] will always be its grandparent view.
+        // TODO(b/306652954) reference the overlay view group directly if we can
+        (ghostView?.parent?.parent as? ViewGroup)?.let {
+            it.clipChildren = false
+            it.clipToPadding = false
+        }
+
         val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
         matrix.getValues(initialGhostViewMatrixValues)
 
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 0a2bd63..7e03bd9 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -45,7 +45,8 @@
     <com.android.systemui.statusbar.LightRevealScrim
         android:id="@+id/light_reveal_scrim"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_height="match_parent"
+        sysui:ignoreRightInset="true" />
 
     <include layout="@layout/status_bar_expanded"
              android:layout_width="match_parent"
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index beea063..8624dd5 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1236,7 +1236,7 @@
     private void onUserStopped(int userId) {
         Slog.d(TAG, "onUserStopped " + userId);
 
-        Watchdog.getInstance().setOneOffTimeoutForMonitors(
+        Watchdog.getInstance().pauseWatchingMonitorsFor(
                 SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
         try {
             mVold.onUserStopped(userId);
@@ -1320,7 +1320,7 @@
                 unlockedUsers.add(userId);
             }
         }
-        Watchdog.getInstance().setOneOffTimeoutForMonitors(
+        Watchdog.getInstance().pauseWatchingMonitorsFor(
                 SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
         for (Integer userId : unlockedUsers) {
             try {
@@ -2341,7 +2341,7 @@
         try {
             // TODO(b/135341433): Remove cautious logging when FUSE is stable
             Slog.i(TAG, "Mounting volume " + vol);
-            Watchdog.getInstance().setOneOffTimeoutForMonitors(
+            Watchdog.getInstance().pauseWatchingMonitorsFor(
                     SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#mount might be slow");
             mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
                 @Override
@@ -2472,7 +2472,7 @@
 
         final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
 
-        Watchdog.getInstance().setOneOffTimeoutForMonitors(
+        Watchdog.getInstance().pauseWatchingMonitorsFor(
                 PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
         try {
             mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);
@@ -2491,7 +2491,7 @@
 
         final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
 
-        Watchdog.getInstance().setOneOffTimeoutForMonitors(
+        Watchdog.getInstance().pauseWatchingMonitorsFor(
                 PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
         try {
             mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1);
@@ -2510,7 +2510,7 @@
 
         final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
 
-        Watchdog.getInstance().setOneOffTimeoutForMonitors(
+        Watchdog.getInstance().pauseWatchingMonitorsFor(
                 PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
         try {
             mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio);
@@ -3620,7 +3620,7 @@
 
         @Override
         public ParcelFileDescriptor open() throws AppFuseMountException {
-            Watchdog.getInstance().setOneOffTimeoutForMonitors(
+            Watchdog.getInstance().pauseWatchingMonitorsFor(
                 SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow");
             try {
                 final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
@@ -3634,7 +3634,7 @@
         @Override
         public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
                 throws AppFuseMountException {
-            Watchdog.getInstance().setOneOffTimeoutForMonitors(
+            Watchdog.getInstance().pauseWatchingMonitorsFor(
                 SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow");
             try {
                 return new ParcelFileDescriptor(
@@ -3646,7 +3646,7 @@
 
         @Override
         public void close() throws Exception {
-            Watchdog.getInstance().setOneOffTimeoutForMonitors(
+            Watchdog.getInstance().pauseWatchingMonitorsFor(
                 SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
             if (mMounted) {
                 mVold.unmountAppFuse(uid, mountId);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 55aa716..003046a 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -250,7 +250,7 @@
         private Monitor mCurrentMonitor;
         private long mStartTimeMillis;
         private int mPauseCount;
-        private long mOneOffTimeoutMillis;
+        private long mPauseEndTimeMillis;
 
         HandlerChecker(Handler handler, String name) {
             mHandler = handler;
@@ -270,20 +270,19 @@
          * @param handlerCheckerTimeoutMillis the timeout to use for this run
          */
         public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) {
-            if (mOneOffTimeoutMillis > 0) {
-              mWaitMaxMillis = mOneOffTimeoutMillis;
-              mOneOffTimeoutMillis = 0;
-            } else {
-              mWaitMaxMillis = handlerCheckerTimeoutMillis;
-            }
+            mWaitMaxMillis = handlerCheckerTimeoutMillis;
 
             if (mCompleted) {
                 // Safe to update monitors in queue, Handler is not in the middle of work
                 mMonitors.addAll(mMonitorQueue);
                 mMonitorQueue.clear();
             }
+
+            long nowMillis = SystemClock.uptimeMillis();
+            boolean isPaused = mPauseCount > 0
+                    || (mPauseEndTimeMillis > 0 && mPauseEndTimeMillis < nowMillis);
             if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
-                    || (mPauseCount > 0)) {
+                    || isPaused) {
                 // Don't schedule until after resume OR
                 // If the target looper has recently been polling, then
                 // there is no reason to enqueue our checker on it since that
@@ -301,7 +300,8 @@
 
             mCompleted = false;
             mCurrentMonitor = null;
-            mStartTimeMillis = SystemClock.uptimeMillis();
+            mStartTimeMillis = nowMillis;
+            mPauseEndTimeMillis = 0;
             mHandler.postAtFrontOfQueue(this);
         }
 
@@ -360,20 +360,19 @@
         }
 
         /**
-         * Sets the timeout of the HandlerChecker for one run.
+         * Pauses the checks for the given time.
          *
-         * <p>The current run will be ignored and the next run will be set to this timeout.
-         *
-         * <p>If a one off timeout is already set, the maximum timeout will be used.
+         * <p>The current run will be ignored and another run will be scheduled after
+         * the given time.
          */
-        public void setOneOffTimeoutLocked(int temporaryTimeoutMillis, String reason) {
-            mOneOffTimeoutMillis = Math.max(temporaryTimeoutMillis, mOneOffTimeoutMillis);
+        public void pauseForLocked(int pauseMillis, String reason) {
+            mPauseEndTimeMillis = SystemClock.uptimeMillis() + pauseMillis;
             // Mark as completed, because there's a chance we called this after the watchog
             // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
             // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
             mCompleted = true;
-            Slog.i(TAG, "Extending timeout of HandlerChecker: " + mName + " for reason: "
-                    + reason + ". New timeout: " + mOneOffTimeoutMillis);
+            Slog.i(TAG, "Pausing of HandlerChecker: " + mName + " for reason: "
+                    + reason + ". Pause end time: " + mPauseEndTimeMillis);
         }
 
         /** Pause the HandlerChecker. */
@@ -623,34 +622,32 @@
     }
 
      /**
-     * Sets a one-off timeout for the next run of the watchdog for this thread. This is useful
+     * Pauses the checks of the watchdog for this thread. This is useful
      * to run a slow operation on one of the monitored thread.
      *
-     * <p>After the next run, the timeout will go back to the default value.
-     *
-     * <p>If the current thread has not been added to the Watchdog, this call is a no-op.
-     *
-     * <p>If a one-off timeout for the current thread is already, the max value will be used.
+     * <p>After the given time, the timeout will go back to the default value.
+     * <p>This method does not require resume to be called.
      */
-    public void setOneOffTimeoutForCurrentThread(int oneOffTimeoutMillis, String reason) {
+    public void pauseWatchingCurrentThreadFor(int pauseMillis, String reason) {
         synchronized (mLock) {
             for (HandlerCheckerAndTimeout hc : mHandlerCheckers) {
                 HandlerChecker checker = hc.checker();
                 if (Thread.currentThread().equals(checker.getThread())) {
-                    checker.setOneOffTimeoutLocked(oneOffTimeoutMillis, reason);
+                    checker.pauseForLocked(pauseMillis, reason);
                 }
             }
         }
     }
 
     /**
-     * Sets a one-off timeout for the next run of the watchdog for the monitor thread.
+     * Pauses the checks of the watchdog for the monitor thread for the given time
      *
-     * <p>Simiar to {@link setOneOffTimeoutForCurrentThread} but used for monitors added through
-     * {@link #addMonitor}
+     * <p>Similar to {@link pauseWatchingCurrentThreadFor} but used for monitors added
+     * through {@link #addMonitor}
+     * <p>This method does not require resume to be called.
      */
-    public void setOneOffTimeoutForMonitors(int oneOffTimeoutMillis, String reason) {
-        mMonitorChecker.setOneOffTimeoutLocked(oneOffTimeoutMillis, reason);
+    public void pauseWatchingMonitorsFor(int pauseMillis, String reason) {
+        mMonitorChecker.pauseForLocked(pauseMillis, reason);
     }
 
     /**
@@ -664,7 +661,7 @@
      * adds another pause and will require an additional {@link #resumeCurrentThread} to resume.
      *
      * <p>Note: Use with care, as any deadlocks on the current thread will be undetected until all
-     * pauses have been resumed. Prefer to use #setOneOffTimeoutForCurrentThread.
+     * pauses have been resumed. Prefer to use #pauseWatchingCurrentThreadFor.
      */
     public void pauseWatchingCurrentThread(String reason) {
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index ba43c8d..292fc14 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -188,7 +188,7 @@
      * {@link AdiDeviceState#toPersistableString()}.
      */
     public static int getPeristedMaxSize() {
-        return 36;  /* (mDeviceType)2 + (mDeviceAddresss)17 + (mInternalDeviceType)9 + (mSAEnabled)1
+        return 36;  /* (mDeviceType)2 + (mDeviceAddress)17 + (mInternalDeviceType)9 + (mSAEnabled)1
                            + (mHasHeadTracker)1 + (mHasHeadTrackerEnabled)1
                            + (SETTINGS_FIELD_SEPARATOR)5 */
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 452be2f4..7ca5699 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -290,7 +290,6 @@
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.MetricsLogger;
@@ -1180,7 +1179,7 @@
         @Override
         public void onSetDisabled(int status) {
             synchronized (mNotificationLock) {
-                if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+                if (Flags.refactorAttentionHelper()) {
                     mAttentionHelper.updateDisableNotificationEffectsLocked(status);
                 } else {
                     mDisableNotificationEffects =
@@ -1326,7 +1325,7 @@
         public void clearEffects() {
             synchronized (mNotificationLock) {
                 if (DBG) Slog.d(TAG, "clearEffects");
-                if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+                if (Flags.refactorAttentionHelper()) {
                     mAttentionHelper.clearAttentionEffects();
                 } else {
                     clearSoundLocked();
@@ -1555,8 +1554,7 @@
                         int changedFlags = data.getFlags() ^ flags;
                         if ((changedFlags & FLAG_SUPPRESS_NOTIFICATION) != 0) {
                             // Suppress notification flag changed, clear any effects
-                            if (mFlagResolver.isEnabled(
-                                    NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+                            if (Flags.refactorAttentionHelper()) {
                                 mAttentionHelper.clearEffectsLocked(key);
                             } else {
                                 clearEffectsLocked(key);
@@ -1905,7 +1903,7 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
-            if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+            if (!Flags.refactorAttentionHelper()) {
                 if (action.equals(Intent.ACTION_SCREEN_ON)) {
                     // Keep track of screen on/off state, but do not turn off the notification light
                     // until user passes through the lock screen or views the notification.
@@ -2019,7 +2017,7 @@
             ContentResolver resolver = getContext().getContentResolver();
             resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
                     false, this, UserHandle.USER_ALL);
-            if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+            if (!Flags.refactorAttentionHelper()) {
                 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
                     false, this, UserHandle.USER_ALL);
             }
@@ -2045,7 +2043,7 @@
 
         public void update(Uri uri) {
             ContentResolver resolver = getContext().getContentResolver();
-            if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+            if (!Flags.refactorAttentionHelper()) {
                 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
                     boolean pulseEnabled = Settings.System.getIntForUser(resolver,
                         Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT)
@@ -2538,7 +2536,7 @@
 
         mToastRateLimiter = toastRateLimiter;
 
-        if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+        if (Flags.refactorAttentionHelper()) {
             mAttentionHelper = new NotificationAttentionHelper(getContext(), lightsManager,
                 mAccessibilityManager, mPackageManagerClient, userManager, usageStats,
                 mNotificationManagerPrivate, mZenModeHelper, flagResolver);
@@ -2548,7 +2546,7 @@
         // If this is called within a test, make sure to unregister the intent receivers by
         // calling onDestroy()
         IntentFilter filter = new IntentFilter();
-        if (!mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+        if (!Flags.refactorAttentionHelper()) {
             filter.addAction(Intent.ACTION_SCREEN_ON);
             filter.addAction(Intent.ACTION_SCREEN_OFF);
             filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
@@ -2876,7 +2874,7 @@
             }
             registerNotificationPreferencesPullers();
             new LockPatternUtils(getContext()).registerStrongAuthTracker(mStrongAuthTracker);
-            if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+            if (Flags.refactorAttentionHelper()) {
                 mAttentionHelper.onSystemReady();
             }
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -6501,7 +6499,7 @@
                     pw.println("  mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
                     pw.println("  hideSilentStatusBar="
                             + mPreferencesHelper.shouldHideSilentStatusIcons());
-                    if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+                    if (Flags.refactorAttentionHelper()) {
                         mAttentionHelper.dump(pw, "    ", filter);
                     }
                 }
@@ -7767,7 +7765,7 @@
             boolean wasPosted = removeFromNotificationListsLocked(r);
             cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null,
                     SystemClock.elapsedRealtime());
-            if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+            if (Flags.refactorAttentionHelper()) {
                 mAttentionHelper.updateLightsLocked();
             } else {
                 updateLightsLocked();
@@ -7900,7 +7898,7 @@
                     cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName,
                             mSendDelete, childrenFlagChecker, mReason,
                             mCancellationElapsedTimeMs);
-                    if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+                    if (Flags.refactorAttentionHelper()) {
                         mAttentionHelper.updateLightsLocked();
                     } else {
                         updateLightsLocked();
@@ -8197,7 +8195,7 @@
 
                     int buzzBeepBlinkLoggingCode = 0;
                     if (!r.isHidden()) {
-                        if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+                        if (Flags.refactorAttentionHelper()) {
                             buzzBeepBlinkLoggingCode = mAttentionHelper.buzzBeepBlinkLocked(r,
                                 new NotificationAttentionHelper.Signals(
                                     mUserProfiles.isCurrentProfile(r.getUserId()),
@@ -9184,7 +9182,7 @@
                     || interruptiveChanged;
             if (interceptBefore && !record.isIntercepted()
                     && record.isNewEnoughForAlerting(System.currentTimeMillis())) {
-                if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+                if (Flags.refactorAttentionHelper()) {
                     mAttentionHelper.buzzBeepBlinkLocked(record,
                         new NotificationAttentionHelper.Signals(
                             mUserProfiles.isCurrentProfile(record.getUserId()), mListenerHints));
@@ -9564,7 +9562,7 @@
                 });
             }
 
-            if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+            if (Flags.refactorAttentionHelper()) {
                 mAttentionHelper.clearEffectsLocked(canceledKey);
             } else {
                 // sound
@@ -9928,7 +9926,7 @@
                             cancellationElapsedTimeMs);
                 }
             }
-            if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
+            if (Flags.refactorAttentionHelper()) {
                 mAttentionHelper.updateLightsLocked();
             } else {
                 updateLightsLocked();
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 06448d0..022ef61 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -28,6 +28,7 @@
 import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.media.projection.IMediaProjectionManager;
 import android.os.IBinder;
@@ -36,10 +37,12 @@
 import android.view.ContentRecordingSession;
 import android.view.ContentRecordingSession.RecordContent;
 import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 /**
  * Manages content recording for a particular {@link DisplayContent}.
@@ -47,6 +50,16 @@
 final class ContentRecorder implements WindowContainerListener {
 
     /**
+     * Maximum acceptable anisotropy for the output image.
+     *
+     * Necessary to avoid unnecessary scaling when the anisotropy is almost the same, as it is not
+     * exact anyway. For external displays, we expect an anisoptry of about 2% even if the pixels
+     * are, in fact, square due to the imprecision of the display's actual size (rounded to the
+     * nearest cm).
+     */
+    private static final float MAX_ANISOTROPY = 0.025f;
+
+    /**
      * The display content this class is handling recording for.
      */
     @NonNull
@@ -87,15 +100,20 @@
     @Configuration.Orientation
     private int mLastOrientation = ORIENTATION_UNDEFINED;
 
+    private final boolean mCorrectForAnisotropicPixels;
+
     ContentRecorder(@NonNull DisplayContent displayContent) {
-        this(displayContent, new RemoteMediaProjectionManagerWrapper(displayContent.mDisplayId));
+        this(displayContent, new RemoteMediaProjectionManagerWrapper(displayContent.mDisplayId),
+                new DisplayManagerFlags().isConnectedDisplayManagementEnabled());
     }
 
     @VisibleForTesting
     ContentRecorder(@NonNull DisplayContent displayContent,
-            @NonNull MediaProjectionManagerWrapper mediaProjectionManager) {
+            @NonNull MediaProjectionManagerWrapper mediaProjectionManager,
+            boolean correctForAnisotropicPixels) {
         mDisplayContent = displayContent;
         mMediaProjectionManager = mediaProjectionManager;
+        mCorrectForAnisotropicPixels = correctForAnisotropicPixels;
     }
 
     /**
@@ -460,6 +478,33 @@
         }
     }
 
+    private void computeScaling(int inputSizeX, int inputSizeY,
+            float inputDpiX, float inputDpiY,
+            int outputSizeX, int outputSizeY,
+            float outputDpiX, float outputDpiY,
+            PointF scaleOut) {
+        float relAnisotropy = (inputDpiY / inputDpiX) / (outputDpiY / outputDpiX);
+        if (!mCorrectForAnisotropicPixels
+                || (relAnisotropy > (1 - MAX_ANISOTROPY) && relAnisotropy < (1 + MAX_ANISOTROPY))) {
+            // Calculate the scale to apply to the root mirror SurfaceControl to fit the size of the
+            // output surface.
+            float scaleX = outputSizeX / (float) inputSizeX;
+            float scaleY = outputSizeY / (float) inputSizeY;
+            float scale = Math.min(scaleX, scaleY);
+            scaleOut.x = scale;
+            scaleOut.y = scale;
+            return;
+        }
+
+        float relDpiX = outputDpiX / inputDpiX;
+        float relDpiY = outputDpiY / inputDpiY;
+
+        float scale = Math.min(outputSizeX / relDpiX / inputSizeX,
+                outputSizeY / relDpiY / inputSizeY);
+        scaleOut.x = scale * relDpiX;
+        scaleOut.y = scale * relDpiY;
+    }
+
     /**
      * Apply transformations to the mirrored surface to ensure the captured contents are scaled to
      * fit and centred in the output surface.
@@ -473,13 +518,19 @@
      */
     @VisibleForTesting void updateMirroredSurface(SurfaceControl.Transaction transaction,
             Rect recordedContentBounds, Point surfaceSize) {
-        // Calculate the scale to apply to the root mirror SurfaceControl to fit the size of the
-        // output surface.
-        float scaleX = surfaceSize.x / (float) recordedContentBounds.width();
-        float scaleY = surfaceSize.y / (float) recordedContentBounds.height();
-        float scale = Math.min(scaleX, scaleY);
-        int scaledWidth = Math.round(scale * (float) recordedContentBounds.width());
-        int scaledHeight = Math.round(scale * (float) recordedContentBounds.height());
+
+        DisplayInfo inputDisplayInfo = mRecordedWindowContainer.mDisplayContent.getDisplayInfo();
+        DisplayInfo outputDisplayInfo = mDisplayContent.getDisplayInfo();
+
+        PointF scale = new PointF();
+        computeScaling(recordedContentBounds.width(), recordedContentBounds.height(),
+                inputDisplayInfo.physicalXDpi, inputDisplayInfo.physicalYDpi,
+                surfaceSize.x, surfaceSize.y,
+                outputDisplayInfo.physicalXDpi, outputDisplayInfo.physicalYDpi,
+                scale);
+
+        int scaledWidth = Math.round(scale.x * (float) recordedContentBounds.width());
+        int scaledHeight = Math.round(scale.y * (float) recordedContentBounds.height());
 
         // Calculate the shift to apply to the root mirror SurfaceControl to centre the mirrored
         // contents in the output surface.
@@ -493,10 +544,10 @@
         }
 
         ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                "Content Recording: Apply transformations of shift %d x %d, scale %f, crop (aka "
-                        + "recorded content size) %d x %d for display %d; display has size %d x "
-                        + "%d; surface has size %d x %d",
-                shiftedX, shiftedY, scale, recordedContentBounds.width(),
+                "Content Recording: Apply transformations of shift %d x %d, scale %f x %f, crop "
+                        + "(aka recorded content size) %d x %d for display %d; display has size "
+                        + "%d x %d; surface has size %d x %d",
+                shiftedX, shiftedY, scale.x, scale.y, recordedContentBounds.width(),
                 recordedContentBounds.height(), mDisplayContent.getDisplayId(),
                 mDisplayContent.getConfiguration().screenWidthDp,
                 mDisplayContent.getConfiguration().screenHeightDp, surfaceSize.x, surfaceSize.y);
@@ -508,7 +559,7 @@
                         recordedContentBounds.height())
                 // Scale the root mirror SurfaceControl, based upon the size difference between the
                 // source (DisplayArea to capture) and output (surface the app reads images from).
-                .setMatrix(mRecordedSurface, scale, 0 /* dtdx */, 0 /* dtdy */, scale)
+                .setMatrix(mRecordedSurface, scale.x, 0 /* dtdx */, 0 /* dtdy */, scale.y)
                 // Position needs to be updated when the mirrored DisplayArea has changed, since
                 // the content will no longer be centered in the output surface.
                 .setPosition(mRecordedSurface, shiftedX /* x */, shiftedY /* y */);
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 6bfd93b..4bb7d63 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -4,6 +4,8 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
+import static com.android.server.job.JobStore.JOB_FILE_SPLIT_PREFIX;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -46,6 +48,7 @@
 import org.junit.runner.RunWith;
 
 import java.io.File;
+import java.nio.file.Files;
 import java.time.Clock;
 import java.time.ZoneOffset;
 import java.util.ArrayList;
@@ -209,6 +212,43 @@
         assertEquals("Incorrect # of persisted tasks.", 0, jobStatusSet.size());
     }
 
+    @Test
+    public void testSkipExtraFiles() throws Exception {
+        setUseSplitFiles(true);
+        final JobInfo task1 = new Builder(8, mComponent)
+                .setRequiresDeviceIdle(true)
+                .setPeriodic(10000L)
+                .setRequiresCharging(true)
+                .setPersisted(true)
+                .build();
+        final JobInfo task2 = new Builder(12, mComponent)
+                .setMinimumLatency(5000L)
+                .setBackoffCriteria(15000L, JobInfo.BACKOFF_POLICY_LINEAR)
+                .setOverrideDeadline(30000L)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
+                .setPersisted(true)
+                .build();
+        final int uid1 = SOME_UID;
+        final int uid2 = uid1 + 1;
+        final JobStatus JobStatus1 = JobStatus.createFromJobInfo(task1, uid1, null, -1, null, null);
+        final JobStatus JobStatus2 = JobStatus.createFromJobInfo(task2, uid2, null, -1, null, null);
+        runWritingJobsToDisk(JobStatus1, JobStatus2);
+
+        final File rootDir = new File(mTestContext.getFilesDir(), "system/job");
+        final File file1 = new File(rootDir, JOB_FILE_SPLIT_PREFIX + uid1 + ".xml");
+        final File file2 = new File(rootDir, JOB_FILE_SPLIT_PREFIX + uid2 + ".xml");
+
+        Files.copy(file1.toPath(),
+                new File(rootDir, JOB_FILE_SPLIT_PREFIX + uid1 + ".xml.bak").toPath());
+        Files.copy(file1.toPath(), new File(rootDir, "random.xml").toPath());
+        Files.copy(file2.toPath(),
+                new File(rootDir, "blah" + JOB_FILE_SPLIT_PREFIX + uid1 + ".xml").toPath());
+
+        JobSet jobStatusSet = new JobSet();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+        assertEquals("Incorrect # of persisted tasks.", 2, jobStatusSet.size());
+    }
+
     /**
      * Test that dynamic constraints aren't written to disk.
      */
@@ -254,22 +294,22 @@
         file = new File(mTestContext.getFilesDir(), "10000");
         assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
 
-        file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX);
+        file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX);
         assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
 
-        file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + "text.xml");
+        file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + "text.xml");
         assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
 
-        file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + ".xml");
+        file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + ".xml");
         assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
 
-        file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + "-10123.xml");
+        file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + "-10123.xml");
         assertEquals(JobStore.INVALID_UID, JobStore.extractUidFromJobFileName(file));
 
-        file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + "1.xml");
+        file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + "1.xml");
         assertEquals(1, JobStore.extractUidFromJobFileName(file));
 
-        file = new File(mTestContext.getFilesDir(), JobStore.JOB_FILE_SPLIT_PREFIX + "101023.xml");
+        file = new File(mTestContext.getFilesDir(), JOB_FILE_SPLIT_PREFIX + "101023.xml");
         assertEquals(101023, JobStore.extractUidFromJobFileName(file));
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 9bd938f..cf8548c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -80,7 +80,9 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
+
 import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
 import com.android.internal.config.sysui.TestableFlagResolver;
 import com.android.internal.logging.InstanceIdSequence;
@@ -93,6 +95,7 @@
 
 import java.util.List;
 import java.util.Objects;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -190,7 +193,7 @@
         assertTrue(mAccessibilityManager.isEnabled());
 
         // TODO (b/291907312): remove feature flag
-        mTestFlagResolver.setFlagOverride(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER);
         // Disable feature flags by default. Tests should enable as needed.
         mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_EXPIRE_BITMAPS);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 16f97f0..6792cfe 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -85,7 +85,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR;
 import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
 import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -614,7 +613,8 @@
                 });
 
         // TODO (b/291907312): remove feature flag
-        mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, false);
+        mSetFlagsRule.disableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER,
+                Flags.FLAG_POLITE_NOTIFICATIONS);
         initNMS();
     }
 
@@ -655,7 +655,7 @@
         verify(mHistoryManager).onBootPhaseAppsCanStart();
 
         // TODO b/291907312: remove feature flag
-        if (mTestFlagResolver.isEnabled(ENABLE_ATTENTION_HELPER_REFACTOR)) {
+        if (Flags.refactorAttentionHelper()) {
             mService.mAttentionHelper.setAudioManager(mAudioManager);
         } else {
             mService.setAudioManager(mAudioManager);
@@ -1692,7 +1692,7 @@
     @Test
     public void testEnqueueNotificationWithTag_WritesExpectedLogs_NAHRefactor() throws Exception {
         // TODO b/291907312: remove feature flag
-        mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER);
         // Cleanup NMS before re-initializing
         if (mService != null) {
             try {
@@ -9155,7 +9155,7 @@
     public void testOnBubbleMetadataChangedToSuppressNotification_soundStopped_NAHRefactor()
         throws Exception {
         // TODO b/291907312: remove feature flag
-        mTestFlagResolver.setFlagOverride(ENABLE_ATTENTION_HELPER_REFACTOR, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER);
         // Cleanup NMS before re-initializing
         if (mService != null) {
             try {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index d2eb1cc..78566fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -84,31 +84,49 @@
     private final ContentRecordingSession mWaitingDisplaySession =
             ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
     private ContentRecordingSession mTaskSession;
-    private static Point sSurfaceSize;
+    private Point mSurfaceSize;
     private ContentRecorder mContentRecorder;
     @Mock private MediaProjectionManagerWrapper mMediaProjectionManagerWrapper;
     private SurfaceControl mRecordedSurface;
 
+    private boolean mHandleAnisotropicDisplayMirroring = false;
+
     @Before public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        // GIVEN SurfaceControl can successfully mirror the provided surface.
-        sSurfaceSize = new Point(
-                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
-                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
-        mRecordedSurface = surfaceControlMirrors(sSurfaceSize);
-
         doReturn(INVALID_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
 
-        // GIVEN the VirtualDisplay associated with the session (so the display has state ON).
+        // Skip unnecessary operations of relayout.
+        spyOn(mWm.mWindowPlacerLocked);
+        doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
+    }
+
+    private void defaultInit() {
+        createContentRecorder(createDefaultDisplayInfo());
+    }
+
+    private DisplayInfo createDefaultDisplayInfo() {
+        return createDisplayInfo(mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
+                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
+    }
+
+    private DisplayInfo createDisplayInfo(int width, int height) {
+        // GIVEN SurfaceControl can successfully mirror the provided surface.
+        mSurfaceSize = new Point(width, height);
+        mRecordedSurface = surfaceControlMirrors(mSurfaceSize);
+
         DisplayInfo displayInfo = mDisplayInfo;
-        displayInfo.logicalWidth = sSurfaceSize.x;
-        displayInfo.logicalHeight = sSurfaceSize.y;
+        displayInfo.logicalWidth = width;
+        displayInfo.logicalHeight = height;
         displayInfo.state = STATE_ON;
+        return displayInfo;
+    }
+
+    private void createContentRecorder(DisplayInfo displayInfo) {
         mVirtualDisplayContent = createNewDisplay(displayInfo);
         final int displayId = mVirtualDisplayContent.getDisplayId();
         mContentRecorder = new ContentRecorder(mVirtualDisplayContent,
-                mMediaProjectionManagerWrapper);
+                mMediaProjectionManagerWrapper, mHandleAnisotropicDisplayMirroring);
         spyOn(mVirtualDisplayContent);
 
         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
@@ -124,14 +142,11 @@
         // GIVEN a session is waiting for the user to review consent.
         mWaitingDisplaySession.setVirtualDisplayId(displayId);
         mWaitingDisplaySession.setWaitingForConsent(true);
-
-        // Skip unnecessary operations of relayout.
-        spyOn(mWm.mWindowPlacerLocked);
-        doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
     }
 
     @Test
     public void testIsCurrentlyRecording() {
+        defaultInit();
         assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
 
         mContentRecorder.updateRecording();
@@ -140,6 +155,7 @@
 
     @Test
     public void testUpdateRecording_display() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
@@ -147,6 +163,7 @@
 
     @Test
     public void testUpdateRecording_display_invalidDisplayIdToMirror() {
+        defaultInit();
         ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
                 INVALID_DISPLAY);
         mContentRecorder.setContentRecordingSession(session);
@@ -156,6 +173,7 @@
 
     @Test
     public void testUpdateRecording_display_noDisplayContentToMirror() {
+        defaultInit();
         doReturn(null).when(
                 mWm.mRoot).getDisplayContent(anyInt());
         mContentRecorder.setContentRecordingSession(mDisplaySession);
@@ -165,6 +183,7 @@
 
     @Test
     public void testUpdateRecording_task_nullToken() {
+        defaultInit();
         ContentRecordingSession session = mTaskSession;
         session.setVirtualDisplayId(mDisplaySession.getVirtualDisplayId());
         session.setTokenToRecord(null);
@@ -176,6 +195,7 @@
 
     @Test
     public void testUpdateRecording_task_noWindowContainer() {
+        defaultInit();
         // Use the window container token of the DisplayContent, rather than task.
         ContentRecordingSession invalidTaskSession = ContentRecordingSession.createTaskSession(
                 new WindowContainer.RemoteToken(mDisplayContent));
@@ -187,6 +207,7 @@
 
     @Test
     public void testUpdateRecording_wasPaused() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
@@ -197,6 +218,7 @@
 
     @Test
     public void testUpdateRecording_waitingForConsent() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mWaitingDisplaySession);
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
@@ -209,6 +231,7 @@
 
     @Test
     public void testOnConfigurationChanged_neverRecording() {
+        defaultInit();
         mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);
 
         verify(mTransaction, never()).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat());
@@ -218,6 +241,7 @@
 
     @Test
     public void testOnConfigurationChanged_resizesSurface() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
         // Ensure a different orientation when we check if something has changed.
@@ -234,13 +258,14 @@
 
     @Test
     public void testOnConfigurationChanged_resizesVirtualDisplay() {
+        defaultInit();
         final int newWidth = 55;
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
         // The user rotates the device, so the host app resizes the virtual display for the capture.
-        resizeDisplay(mDisplayContent, newWidth, sSurfaceSize.y);
-        resizeDisplay(mVirtualDisplayContent, newWidth, sSurfaceSize.y);
+        resizeDisplay(mDisplayContent, newWidth, mSurfaceSize.y);
+        resizeDisplay(mVirtualDisplayContent, newWidth, mSurfaceSize.y);
         mContentRecorder.onConfigurationChanged(mDisplayContent.getConfiguration().orientation);
 
         verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
@@ -251,6 +276,7 @@
 
     @Test
     public void testOnConfigurationChanged_rotateVirtualDisplay() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
@@ -271,12 +297,13 @@
      */
     @Test
     public void testOnConfigurationChanged_resizeSurface() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
         // Resize the output surface.
-        final Point newSurfaceSize = new Point(Math.round(sSurfaceSize.x / 2f),
-                Math.round(sSurfaceSize.y * 2));
+        final Point newSurfaceSize = new Point(Math.round(mSurfaceSize.x / 2f),
+                Math.round(mSurfaceSize.y * 2));
         doReturn(newSurfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
                 anyInt());
         mContentRecorder.onConfigurationChanged(
@@ -292,6 +319,7 @@
 
     @Test
     public void testOnTaskOrientationConfigurationChanged_resizesSurface() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mContentRecorder.updateRecording();
 
@@ -314,6 +342,7 @@
 
     @Test
     public void testOnTaskBoundsConfigurationChanged_notifiesCallback() {
+        defaultInit();
         mTask.getRootTask().setWindowingMode(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
 
         final int minWidth = 222;
@@ -351,6 +380,7 @@
 
     @Test
     public void testTaskWindowingModeChanged_pip_stopsRecording() {
+        defaultInit();
         // WHEN a recording is ongoing.
         mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         mContentRecorder.setContentRecordingSession(mTaskSession);
@@ -368,6 +398,7 @@
 
     @Test
     public void testTaskWindowingModeChanged_fullscreen_startsRecording() {
+        defaultInit();
         // WHEN a recording is ongoing.
         mTask.setWindowingMode(WINDOWING_MODE_PINNED);
         mContentRecorder.setContentRecordingSession(mTaskSession);
@@ -384,6 +415,7 @@
 
     @Test
     public void testStartRecording_notifiesCallback_taskSession() {
+        defaultInit();
         // WHEN a recording is ongoing.
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mContentRecorder.updateRecording();
@@ -396,6 +428,7 @@
 
     @Test
     public void testStartRecording_notifiesCallback_displaySession() {
+        defaultInit();
         // WHEN a recording is ongoing.
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
@@ -408,6 +441,7 @@
 
     @Test
     public void testStartRecording_taskInPIP_recordingNotStarted() {
+        defaultInit();
         // GIVEN a task is in PIP.
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mTask.setWindowingMode(WINDOWING_MODE_PINNED);
@@ -421,6 +455,7 @@
 
     @Test
     public void testStartRecording_taskInSplit_recordingStarted() {
+        defaultInit();
         // GIVEN a task is in PIP.
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -434,6 +469,7 @@
 
     @Test
     public void testStartRecording_taskInFullscreen_recordingStarted() {
+        defaultInit();
         // GIVEN a task is in PIP.
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -447,6 +483,7 @@
 
     @Test
     public void testOnVisibleRequestedChanged_notifiesCallback() {
+        defaultInit();
         // WHEN a recording is ongoing.
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mContentRecorder.updateRecording();
@@ -471,6 +508,7 @@
 
     @Test
     public void testOnVisibleRequestedChanged_noRecording_doesNotNotifyCallback() {
+        defaultInit();
         // WHEN a recording is not ongoing.
         assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
 
@@ -493,6 +531,7 @@
 
     @Test
     public void testPauseRecording_pausesRecording() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
@@ -502,12 +541,14 @@
 
     @Test
     public void testPauseRecording_neverRecording() {
+        defaultInit();
         mContentRecorder.pauseRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
     }
 
     @Test
     public void testStopRecording_stopsRecording() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
@@ -517,12 +558,14 @@
 
     @Test
     public void testStopRecording_neverRecording() {
+        defaultInit();
         mContentRecorder.stopRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
     }
 
     @Test
     public void testRemoveTask_stopsRecording() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mContentRecorder.updateRecording();
 
@@ -533,6 +576,7 @@
 
     @Test
     public void testRemoveTask_stopsRecording_nullSessionShouldNotThrowExceptions() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mTaskSession);
         mContentRecorder.updateRecording();
         mContentRecorder.setContentRecordingSession(null);
@@ -541,6 +585,7 @@
 
     @Test
     public void testUpdateMirroredSurface_capturedAreaResized() {
+        defaultInit();
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
@@ -548,9 +593,9 @@
         // WHEN attempting to mirror on the virtual display, and the captured content is resized.
         float xScale = 0.7f;
         float yScale = 2f;
-        Rect displayAreaBounds = new Rect(0, 0, Math.round(sSurfaceSize.x * xScale),
-                Math.round(sSurfaceSize.y * yScale));
-        mContentRecorder.updateMirroredSurface(mTransaction, displayAreaBounds, sSurfaceSize);
+        Rect displayAreaBounds = new Rect(0, 0, Math.round(mSurfaceSize.x * xScale),
+                Math.round(mSurfaceSize.y * yScale));
+        mContentRecorder.updateMirroredSurface(mTransaction, displayAreaBounds, mSurfaceSize);
         assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
 
         // THEN content in the captured DisplayArea is scaled to fit the surface size.
@@ -558,7 +603,7 @@
                 1.0f / yScale);
         // THEN captured content is positioned in the centre of the output surface.
         int scaledWidth = Math.round((float) displayAreaBounds.width() / xScale);
-        int xInset = (sSurfaceSize.x - scaledWidth) / 2;
+        int xInset = (mSurfaceSize.x - scaledWidth) / 2;
         verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, xInset, 0);
         // THEN the resize callback is notified.
         verify(mMediaProjectionManagerWrapper).notifyActiveProjectionCapturedContentResized(
@@ -566,7 +611,131 @@
     }
 
     @Test
+    public void testUpdateMirroredSurface_isotropicPixel() {
+        mHandleAnisotropicDisplayMirroring = false;
+        DisplayInfo displayInfo = createDefaultDisplayInfo();
+        createContentRecorder(displayInfo);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, 1, 0, 0, 1);
+    }
+
+    @Test
+    public void testUpdateMirroredSurface_anisotropicPixel_compressY() {
+        mHandleAnisotropicDisplayMirroring = true;
+        DisplayInfo displayInfo = createDefaultDisplayInfo();
+        DisplayInfo inputDisplayInfo =
+                mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+        displayInfo.physicalXDpi = 2.0f * inputDisplayInfo.physicalXDpi;
+        displayInfo.physicalYDpi = inputDisplayInfo.physicalYDpi;
+        createContentRecorder(displayInfo);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        float xScale = 1f;
+        float yScale = 0.5f;
+        verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+                yScale);
+        verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, 0,
+                Math.round(0.25 * mSurfaceSize.y));
+    }
+
+    @Test
+    public void testUpdateMirroredSurface_anisotropicPixel_compressX() {
+        mHandleAnisotropicDisplayMirroring = true;
+        DisplayInfo displayInfo = createDefaultDisplayInfo();
+        DisplayInfo inputDisplayInfo =
+                mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+        displayInfo.physicalXDpi = inputDisplayInfo.physicalXDpi;
+        displayInfo.physicalYDpi = 2.0f * inputDisplayInfo.physicalYDpi;
+        createContentRecorder(displayInfo);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        float xScale = 0.5f;
+        float yScale = 1f;
+        verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+                yScale);
+        verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface,
+                Math.round(0.25 * mSurfaceSize.x), 0);
+    }
+
+    @Test
+    public void testUpdateMirroredSurface_anisotropicPixel_scaleOnX() {
+        mHandleAnisotropicDisplayMirroring = true;
+        int width = 2 * mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width();
+        int height = 6 * mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height();
+        DisplayInfo displayInfo = createDisplayInfo(width, height);
+        DisplayInfo inputDisplayInfo =
+                mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+        displayInfo.physicalXDpi = inputDisplayInfo.physicalXDpi;
+        displayInfo.physicalYDpi = 2.0f * inputDisplayInfo.physicalYDpi;
+        createContentRecorder(displayInfo);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        float xScale = 2f;
+        float yScale = 4f;
+        verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+                yScale);
+        verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, 0,
+                inputDisplayInfo.logicalHeight);
+    }
+
+    @Test
+    public void testUpdateMirroredSurface_anisotropicPixel_scaleOnY() {
+        mHandleAnisotropicDisplayMirroring = true;
+        int width = 6 * mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width();
+        int height = 2 * mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height();
+        DisplayInfo displayInfo = createDisplayInfo(width, height);
+        DisplayInfo inputDisplayInfo =
+                mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+        displayInfo.physicalXDpi = 2.0f * inputDisplayInfo.physicalXDpi;
+        displayInfo.physicalYDpi = inputDisplayInfo.physicalYDpi;
+        createContentRecorder(displayInfo);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        float xScale = 4f;
+        float yScale = 2f;
+        verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+                yScale);
+        verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface,
+                inputDisplayInfo.logicalWidth, 0);
+    }
+
+    @Test
+    public void testUpdateMirroredSurface_anisotropicPixel_shrinkCanvas() {
+        mHandleAnisotropicDisplayMirroring = true;
+        int width = mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width() / 2;
+        int height = mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height() / 2;
+        DisplayInfo displayInfo = createDisplayInfo(width, height);
+        DisplayInfo inputDisplayInfo =
+                mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY).getDisplayInfo();
+        displayInfo.physicalXDpi = 2f * inputDisplayInfo.physicalXDpi;
+        displayInfo.physicalYDpi = inputDisplayInfo.physicalYDpi;
+        createContentRecorder(displayInfo);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        float xScale = 0.5f;
+        float yScale = 0.25f;
+        verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, xScale, 0, 0,
+                yScale);
+        verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, 0,
+                (mSurfaceSize.y - height / 2) / 2);
+    }
+
+    @Test
     public void testDisplayContentUpdatesRecording_withoutSurface() {
+        defaultInit();
         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
         // mirror.
         setUpDefaultTaskDisplayAreaWindowToken();
@@ -585,6 +754,7 @@
 
     @Test
     public void testDisplayContentUpdatesRecording_withSurface() {
+        defaultInit();
         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
         // mirror.
         setUpDefaultTaskDisplayAreaWindowToken();
@@ -602,12 +772,13 @@
 
     @Test
     public void testDisplayContentUpdatesRecording_displayMirroring() {
+        defaultInit();
         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
         // mirror.
         setUpDefaultTaskDisplayAreaWindowToken();
 
         // GIVEN SurfaceControl can successfully mirror the provided surface.
-        surfaceControlMirrors(sSurfaceSize);
+        surfaceControlMirrors(mSurfaceSize);
         // Initially disable getDisplayIdToMirror since the DMS may create the DC outside the direct
         // call in the test. We need to spy on the DC before updateRecording is called or we can't
         // verify setDisplayMirroring is called
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7a0bf90..540cecf 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1056,6 +1056,14 @@
             "carrier_use_ims_first_for_emergency_bool";
 
     /**
+     * When {@code true}, this carrier will preferentially dial normal routed emergency calls over
+     * an in-service SIM if one is available.
+     * @hide
+     */
+    public static final String KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL =
+            "prefer_in_service_sim_for_normal_routed_emergency_calls_bool";
+
+    /**
      * When {@code true}, the determination of whether to place a call as an emergency call will be
      * based on the known {@link android.telephony.emergency.EmergencyNumber}s for the SIM on which
      * the call is being placed. In a dual SIM scenario, if Sim A has the emergency numbers
@@ -9874,6 +9882,8 @@
         sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+        sDefaults.putBoolean(KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL,
+                false);
         sDefaults.putBoolean(KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL, false);
         sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
         sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");