Merge "Revert "Require network permission for networked jobs."" into udc-dev
diff --git a/cmds/gpu_counter_producer/Android.bp b/cmds/gpu_counter_producer/Android.bp
new file mode 100644
index 0000000..5b118ce
--- /dev/null
+++ b/cmds/gpu_counter_producer/Android.bp
@@ -0,0 +1,26 @@
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_binary {
+    name: "gpu_counter_producer",
+
+    srcs: ["main.cpp"],
+
+    shared_libs: [
+        "libdl",
+        "liblog",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+        "-fPIE",
+        "-pie",
+    ],
+
+    soc_specific: true,
+}
diff --git a/cmds/gpu_counter_producer/OWNERS b/cmds/gpu_counter_producer/OWNERS
new file mode 100644
index 0000000..892c2fb
--- /dev/null
+++ b/cmds/gpu_counter_producer/OWNERS
@@ -0,0 +1 @@
+pmuetschard@google.com
diff --git a/cmds/gpu_counter_producer/main.cpp b/cmds/gpu_counter_producer/main.cpp
new file mode 100644
index 0000000..1054cba
--- /dev/null
+++ b/cmds/gpu_counter_producer/main.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "gpu_counters"
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define _LOG(level, msg, ...)                                 \
+    do {                                                      \
+        fprintf(stderr, #level ": " msg "\n", ##__VA_ARGS__); \
+        ALOG##level(msg, ##__VA_ARGS__);                      \
+    } while (false)
+
+#define LOG_ERR(msg, ...) _LOG(E, msg, ##__VA_ARGS__)
+#define LOG_WARN(msg, ...) _LOG(W, msg, ##__VA_ARGS__)
+#define LOG_INFO(msg, ...) _LOG(I, msg, ##__VA_ARGS__)
+
+#define NELEM(x) (sizeof(x) / sizeof(x[0]))
+
+typedef void (*FN_PTR)(void);
+
+const char* kProducerPaths[] = {
+        "libgpudataproducer.so",
+};
+const char* kPidFileName = "/data/local/tmp/gpu_counter_producer.pid";
+
+static FN_PTR loadLibrary(const char* lib) {
+    char* error;
+
+    LOG_INFO("Trying %s", lib);
+    void* handle = dlopen(lib, RTLD_GLOBAL);
+    if ((error = dlerror()) != nullptr || handle == nullptr) {
+        LOG_WARN("Error loading lib: %s", error);
+        return nullptr;
+    }
+
+    FN_PTR startFunc = (FN_PTR)dlsym(handle, "start");
+    if ((error = dlerror()) != nullptr) {
+        LOG_ERR("Error looking for start symbol: %s", error);
+        dlclose(handle);
+        return nullptr;
+    }
+    return startFunc;
+}
+
+static void killExistingProcess() {
+    int fd = open(kPidFileName, O_RDONLY);
+    if (fd == -1) {
+        return;
+    }
+    char pidString[10];
+    if (read(fd, pidString, 10) > 0) {
+        int pid = -1;
+        sscanf(pidString, "%d", &pid);
+        if (pid > 0) {
+            kill(pid, SIGINT);
+        }
+    }
+    close(fd);
+}
+
+static bool writeToPidFile() {
+    killExistingProcess();
+    int fd = open(kPidFileName, O_CREAT | O_WRONLY | O_TRUNC,
+                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+    if (fd == -1) {
+        return false;
+    }
+    pid_t pid = getpid();
+    char pidString[10];
+    sprintf(pidString, "%d", pid);
+    write(fd, pidString, strlen(pidString));
+    close(fd);
+    return true;
+}
+
+static void clearPidFile() {
+    unlink(kPidFileName);
+}
+
+static void usage(const char* pname) {
+    fprintf(stderr,
+            "Starts the GPU hardware counter profiling Perfetto data producer.\n\n"
+            "usage: %s [-hf]\n"
+            "   -f: run in the foreground.\n"
+            "   -h: this message.\n",
+            pname);
+}
+
+// Program to load the GPU Perfetto producer .so and call start().
+int main(int argc, char** argv) {
+    const char* pname = argv[0];
+    bool foreground = false;
+    int c;
+    while ((c = getopt(argc, argv, "fh")) != -1) {
+        switch (c) {
+            case 'f':
+                foreground = true;
+                break;
+            case '?':
+            case ':':
+            case 'h':
+                usage(pname);
+                return 1;
+        }
+    }
+
+    if (optind < argc) {
+        usage(pname);
+        return 1;
+    }
+
+    if (!foreground) {
+        daemon(0, 0);
+    }
+
+    if (!writeToPidFile()) {
+        LOG_ERR("Could not open %s", kPidFileName);
+        return 1;
+    }
+
+    dlerror(); // Clear any possibly ignored previous error.
+    FN_PTR startFunc = nullptr;
+    for (int i = 0; startFunc == nullptr && i < NELEM(kProducerPaths); i++) {
+        startFunc = loadLibrary(kProducerPaths[i]);
+    }
+
+    if (startFunc == nullptr) {
+        LOG_ERR("Did not find the producer library");
+        LOG_ERR("LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
+        clearPidFile();
+        return 1;
+    }
+
+    LOG_INFO("Calling start at %p", startFunc);
+    (*startFunc)();
+    LOG_WARN("Producer has exited.");
+
+    clearPidFile();
+    return 0;
+}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 7107bf7..e93467b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -415,6 +415,7 @@
     method public void clickNotification(@Nullable String, int, int, boolean);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
     method public void expandNotificationsPanel();
+    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getLastSystemKey();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(int);
     method public void sendNotificationFeedback(@Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
@@ -2628,6 +2629,7 @@
     field public static final String SELECTED_SPELL_CHECKER_SUBTYPE = "selected_spell_checker_subtype";
     field public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option";
     field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
+    field public static final String STYLUS_BUTTONS_ENABLED = "stylus_buttons_enabled";
     field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
     field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
   }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index f74be22..29f774c 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -752,6 +752,29 @@
     }
 
     /**
+     * Gets the last handled system key. A system key is a KeyEvent that the
+     * {@link com.android.server.policy.PhoneWindowManager} sends directly to the
+     * status bar, rather than forwarding to apps. If a key has never been sent to the
+     * status bar, will return -1.
+     *
+     * @return the keycode of the last KeyEvent that has been sent to the system.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.STATUS_BAR)
+    @TestApi
+    public int getLastSystemKey() {
+        try {
+            final IStatusBarService svc = getService();
+            if (svc != null) {
+                return svc.getLastSystemKey();
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+        return -1;
+    }
+
+    /**
      * Expand the settings panel.
      *
      * @hide
diff --git a/core/java/android/hardware/DataSpace.java b/core/java/android/hardware/DataSpace.java
index b8b1eaa..312bfdf 100644
--- a/core/java/android/hardware/DataSpace.java
+++ b/core/java/android/hardware/DataSpace.java
@@ -16,6 +16,7 @@
 package android.hardware;
 
 import android.annotation.IntDef;
+import android.view.SurfaceControl;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -376,12 +377,19 @@
      */
     public static final int RANGE_LIMITED = 2 << 27;
     /**
-     * Extended range is used for scRGB only.
+     * Extended range can be used in combination with FP16 to communicate scRGB or with
+     * {@link android.view.SurfaceControl.Transaction#setExtendedRangeBrightness(SurfaceControl, float, float)}
+     * to indicate an HDR range.
      *
-     * <p>Intended for use with floating point pixel formats. [0.0 - 1.0] is the standard
-     * sRGB space. Values outside the range [0.0 - 1.0] can encode
-     * color outside the sRGB gamut. [-0.5, 7.5] is the scRGB range.
+     * <p>When used with floating point pixel formats and #STANDARD_BT709 then [0.0 - 1.0] is the
+     * standard sRGB space and values outside the range [0.0 - 1.0] can encode
+     * color outside the sRGB gamut. [-0.5, 7.5] is the standard scRGB range.
      * Used to blend/merge multiple dataspaces on a single display.</p>
+     *
+     * <p>As of {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} this may be combined with
+     * {@link android.view.SurfaceControl.Transaction#setExtendedRangeBrightness(SurfaceControl, float, float)}
+     * and other formats such as {@link HardwareBuffer#RGBA_8888} or
+     * {@link HardwareBuffer#RGBA_1010102} to communicate a variable HDR brightness range</p>
      */
     public static final int RANGE_EXTENDED = 3 << 27;
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 893fce2..7ae280f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7414,6 +7414,8 @@
          *
          * @hide
          */
+        @TestApi
+        @Readable
         @SuppressLint("NoSettingsProvider")
         public static final String STYLUS_BUTTONS_ENABLED = "stylus_buttons_enabled";
 
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index b74b80e..7ad43c7 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -162,7 +162,7 @@
         return WindowMetrics.class.getSimpleName() + ":{"
                 + "bounds=" + mBounds
                 + ", windowInsets=" + mWindowInsets
-                + ", density" + mDensity
+                + ", density=" + mDensity
                 + "}";
     }
 }
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index e267a7f..e51eff4 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -150,6 +150,15 @@
             "package_deny_list_for_unimportant_view";
 
     /**
+     * Sets the list of activities and packages allowed for autofill. The format is same with
+     * {@link #DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW}
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_PACKAGE_AND_ACTIVITY_ALLOWLIST_FOR_TRIGGERING_FILL_REQUEST =
+            "package_and_activity_allowlist_for_triggering_fill_request";
+
+    /**
      * Whether the heuristics check for view is enabled
      */
     public static final String DEVICE_CONFIG_TRIGGER_FILL_REQUEST_ON_UNIMPORTANT_VIEW =
@@ -183,6 +192,7 @@
      */
     public static final String DEVICE_CONFIG_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES =
             "should_enable_autofill_on_all_view_types";
+
     // END AUTOFILL FOR ALL APPS FLAGS //
 
 
@@ -378,6 +388,16 @@
             DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW, "");
     }
 
+    /**
+     * Get autofill allowlist from flag
+     *
+     * @hide
+     */
+    public static String getAllowlistStringFromFlag() {
+        return DeviceConfig.getString(
+            DeviceConfig.NAMESPACE_AUTOFILL,
+            DEVICE_CONFIG_PACKAGE_AND_ACTIVITY_ALLOWLIST_FOR_TRIGGERING_FILL_REQUEST, "");
+    }
 
     // START AUTOFILL PCC CLASSIFICATION FUNCTIONS
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index c5befb6..cc8ab10 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -694,7 +694,18 @@
     private boolean mIsPackagePartiallyDeniedForAutofill = false;
 
     // A deny set read from device config
-    private Set<String> mDeniedActivitiySet = new ArraySet<>();
+    private Set<String> mDeniedActivitySet = new ArraySet<>();
+
+    // If a package is fully allowed, all views in package will skip the heuristic check
+    private boolean mIsPackageFullyAllowedForAutofill = false;
+
+    // If a package is partially denied, autofill manager will check whether
+    // current activity is in allowed activity set. If it's allowed activity, then autofill manager
+    // will skip the heuristic check
+    private boolean mIsPackagePartiallyAllowedForAutofill = false;
+
+    // An allowed activity set read from device config
+    private Set<String> mAllowedActivitySet = new ArraySet<>();
 
     // Indicates whether called the showAutofillDialog() method.
     private boolean mShowAutofillDialogCalled = false;
@@ -873,19 +884,34 @@
             AutofillFeatureFlags.getNonAutofillableImeActionIdSetFromFlag();
 
         final String denyListString = AutofillFeatureFlags.getDenylistStringFromFlag();
+        final String allowlistString = AutofillFeatureFlags.getAllowlistStringFromFlag();
 
         final String packageName = mContext.getPackageName();
 
         mIsPackageFullyDeniedForAutofill =
-            isPackageFullyDeniedForAutofill(denyListString, packageName);
+            isPackageFullyAllowedOrDeniedForAutofill(denyListString, packageName);
+
+        mIsPackageFullyAllowedForAutofill =
+            isPackageFullyAllowedOrDeniedForAutofill(allowlistString, packageName);
 
         if (!mIsPackageFullyDeniedForAutofill) {
             mIsPackagePartiallyDeniedForAutofill =
-                isPackagePartiallyDeniedForAutofill(denyListString, packageName);
+                isPackagePartiallyDeniedOrAllowedForAutofill(denyListString, packageName);
+        }
+
+        if (!mIsPackageFullyAllowedForAutofill) {
+            mIsPackagePartiallyAllowedForAutofill =
+                isPackagePartiallyDeniedOrAllowedForAutofill(allowlistString, packageName);
         }
 
         if (mIsPackagePartiallyDeniedForAutofill) {
-            setDeniedActivitySetWithDenyList(denyListString, packageName);
+            mDeniedActivitySet = getDeniedOrAllowedActivitySetFromString(
+                    denyListString, packageName);
+        }
+
+        if (mIsPackagePartiallyAllowedForAutofill) {
+            mAllowedActivitySet = getDeniedOrAllowedActivitySetFromString(
+                    allowlistString, packageName);
         }
     }
 
@@ -921,59 +947,59 @@
         return true;
     }
 
-    private boolean isPackageFullyDeniedForAutofill(
-            @NonNull String denyListString, @NonNull String packageName) {
-        // If "PackageName:;" is in the string, then it means the package name is in denylist
-        // and there are no activities specified under it. That means the package is fully
-        // denied for autofill
-        return denyListString.indexOf(packageName + ":;") != -1;
+    private boolean isPackageFullyAllowedOrDeniedForAutofill(
+            @NonNull String listString, @NonNull String packageName) {
+        // If "PackageName:;" is in the string, then it the package is fully denied or allowed for
+        // autofill, depending on which string is passed to this function
+        return listString.indexOf(packageName + ":;") != -1;
     }
 
-    private boolean isPackagePartiallyDeniedForAutofill(
-            @NonNull String denyListString, @NonNull String packageName) {
-        // This check happens after checking package is not fully denied. If "PackageName:" instead
-        // is in denylist, then it means there are specific activities to be denied. So the package
-        // is partially denied for autofill
-        return denyListString.indexOf(packageName + ":") != -1;
+    private boolean isPackagePartiallyDeniedOrAllowedForAutofill(
+            @NonNull String listString, @NonNull String packageName) {
+        // If "PackageName:" is in string when "PackageName:;" is not, then it means there are
+        // specific activities to be allowed or denied. So the package is partially allowed or
+        // denied for autofill.
+        return listString.indexOf(packageName + ":") != -1;
     }
 
     /**
-     * Get the denied activitiy names under specified package from denylist and set it in field
-     * mDeniedActivitiySet
+     * Get the denied or allowed activitiy names under specified package from the list string and
+     * set it in fields accordingly
      *
-     * If using parameter as the example below, the denied activity set would be set to
-     * Set{Activity1,Activity2}.
+     * For example, if the package name is Package1, and the string is
+     * "Package1:Activity1,Activity2;", then the extracted activity set would be
+     * {Activity1, Activity2}
      *
-     * @param denyListString Denylist that is got from device config. For example,
+     * @param listString Denylist that is got from device config. For example,
      *        "Package1:Activity1,Activity2;Package2:;"
-     * @param packageName Specify to extract activities under which package.For example,
-     *        "Package1:;"
+     * @param packageName Specify which package to extract.For example, "Package1"
+     *
+     * @return the extracted activity set, For example, {Activity1, Activity2}
      */
-    private void setDeniedActivitySetWithDenyList(
-            @NonNull String denyListString, @NonNull String packageName) {
+    private Set<String> getDeniedOrAllowedActivitySetFromString(
+            @NonNull String listString, @NonNull String packageName) {
         // 1. Get the index of where the Package name starts
-        final int packageInStringIndex = denyListString.indexOf(packageName + ":");
+        final int packageInStringIndex = listString.indexOf(packageName + ":");
 
         // 2. Get the ";" index after this index of package
-        final int firstNextSemicolonIndex = denyListString.indexOf(";", packageInStringIndex);
+        final int firstNextSemicolonIndex = listString.indexOf(";", packageInStringIndex);
 
         // 3. Get the activity names substring between the indexes
         final int activityStringStartIndex = packageInStringIndex + packageName.length() + 1;
+
         if (activityStringStartIndex >= firstNextSemicolonIndex) {
-            Log.e(TAG, "Failed to get denied activity names from denylist because it's wrongly "
+            Log.e(TAG, "Failed to get denied activity names from list because it's wrongly "
                     + "formatted");
-            return;
+            return new ArraySet<>();
         }
         final String activitySubstring =
-                denyListString.substring(activityStringStartIndex, firstNextSemicolonIndex);
+                listString.substring(activityStringStartIndex, firstNextSemicolonIndex);
 
         // 4. Split the activity name substring
         final String[] activityStringArray = activitySubstring.split(",");
 
-        // 5. Set the denied activity set
-        mDeniedActivitiySet = new ArraySet<>(Arrays.asList(activityStringArray));
-
-        return;
+        // 5. return the extracted activities in a set
+        return new ArraySet<>(Arrays.asList(activityStringArray));
     }
 
     /**
@@ -992,7 +1018,32 @@
                 return false;
             }
             final ComponentName clientActivity = client.autofillClientGetComponentName();
-            if (mDeniedActivitiySet.contains(clientActivity.flattenToShortString())) {
+            if (mDeniedActivitySet.contains(clientActivity.flattenToShortString())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check whether current activity is allowlisted for autofill.
+     *
+     * If it is, the view in current activity will bypass heuristic check when checking whether it's
+     * autofillable
+     *
+     * @hide
+     */
+    public boolean isActivityAllowedForAutofill() {
+        if (mIsPackageFullyAllowedForAutofill) {
+            return true;
+        }
+        if (mIsPackagePartiallyAllowedForAutofill) {
+            final AutofillClient client = getClient();
+            if (client == null) {
+                return false;
+            }
+            final ComponentName clientActivity = client.autofillClientGetComponentName();
+            if (mAllowedActivitySet.contains(clientActivity.flattenToShortString())) {
                 return true;
             }
         }
@@ -1009,17 +1060,22 @@
      * @hide
      */
     public boolean isAutofillable(View view) {
-        if (isActivityDeniedForAutofill()) {
-            Log.d(TAG, "view is not autofillable - activity denied for autofill");
-            return false;
-        }
-
         // Duplicate the autofill type check here because ViewGroup will call this function to
         // decide whether to include view in assist structure.
         // Also keep the autofill type check inside View#IsAutofillable() to serve as an early out
         // or if other functions need to call it.
         if (view.getAutofillType() == View.AUTOFILL_TYPE_NONE) return false;
 
+        if (isActivityDeniedForAutofill()) {
+            Log.d(TAG, "view is not autofillable - activity denied for autofill");
+            return false;
+        }
+
+        if (isActivityAllowedForAutofill()) {
+            Log.d(TAG, "view is autofillable - activity allowed for autofill");
+            return true;
+        }
+
         if (view instanceof EditText) {
             return isPassingImeActionCheck((EditText) view);
         }
@@ -1037,7 +1093,7 @@
             || view instanceof RadioGroup) {
             return true;
         }
-
+        Log.d(TAG, "view is not autofillable - not important and filtered by view type check");
         return false;
     }
 
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 5140594..e0ee683 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -16,6 +16,8 @@
 
 package android.window;
 
+import android.annotation.AnimRes;
+import android.annotation.ColorInt;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -245,6 +247,9 @@
     public static final class CustomAnimationInfo implements Parcelable {
         private final String mPackageName;
         private int mWindowAnimations;
+        @AnimRes private int mCustomExitAnim;
+        @AnimRes private int mCustomEnterAnim;
+        @ColorInt private int mCustomBackground;
 
         /**
          * The package name of the windowAnimations.
@@ -261,6 +266,27 @@
             return mWindowAnimations;
         }
 
+        /**
+         * The exit animation resource Id of customize activity transition.
+         */
+        public int getCustomExitAnim() {
+            return mCustomExitAnim;
+        }
+
+        /**
+         * The entering animation resource Id of customize activity transition.
+         */
+        public int getCustomEnterAnim() {
+            return mCustomEnterAnim;
+        }
+
+        /**
+         * The background color of customize activity transition.
+         */
+        public int getCustomBackground() {
+            return mCustomBackground;
+        }
+
         public CustomAnimationInfo(@NonNull String packageName) {
             this.mPackageName = packageName;
         }
@@ -274,11 +300,17 @@
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeString8(mPackageName);
             dest.writeInt(mWindowAnimations);
+            dest.writeInt(mCustomEnterAnim);
+            dest.writeInt(mCustomExitAnim);
+            dest.writeInt(mCustomBackground);
         }
 
         private CustomAnimationInfo(@NonNull Parcel in) {
             mPackageName = in.readString8();
             mWindowAnimations = in.readInt();
+            mCustomEnterAnim = in.readInt();
+            mCustomExitAnim = in.readInt();
+            mCustomBackground = in.readInt();
         }
 
         @Override
@@ -349,10 +381,25 @@
          * Set windowAnimations for customize animation.
          */
         public Builder setWindowAnimations(String packageName, int windowAnimations) {
-            mCustomAnimationInfo = new CustomAnimationInfo(packageName);
+            if (mCustomAnimationInfo == null) {
+                mCustomAnimationInfo = new CustomAnimationInfo(packageName);
+            }
             mCustomAnimationInfo.mWindowAnimations = windowAnimations;
             return this;
         }
+        /**
+         * Set resources ids for customize activity animation.
+         */
+        public Builder setCustomAnimation(String packageName, @AnimRes int enterResId,
+                @AnimRes int exitResId, @ColorInt int backgroundColor) {
+            if (mCustomAnimationInfo == null) {
+                mCustomAnimationInfo = new CustomAnimationInfo(packageName);
+            }
+            mCustomAnimationInfo.mCustomExitAnim = exitResId;
+            mCustomAnimationInfo.mCustomEnterAnim = enterResId;
+            mCustomAnimationInfo.mCustomBackground = backgroundColor;
+            return this;
+        }
 
         /**
          * Builds and returns an instance of {@link BackNavigationInfo}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index b83d1d8..f19f6c7 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -87,6 +87,8 @@
             new ComponentName("com.android.server.accessibility", "OneHandedMode");
     public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
+    public static final ComponentName FONT_SIZE_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "FontSize");
 
     // The component name for the sub setting of Accessibility button in Accessibility settings
     public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 3226669..1c0da18 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -328,6 +328,7 @@
         mTracingStarted = true;
         markEvent("FT#begin");
         Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+        markEvent("FT#layerId#" + mSurfaceControl.getLayerId());
         mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
         if (!mSurfaceOnly) {
             mRendererWrapper.addObserver(mObserver);
@@ -437,8 +438,10 @@
                     "The length of the trace event description <%s> exceeds %d",
                     desc, MAX_LENGTH_EVENT_DESC));
         }
-        Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
-        Trace.endSection();
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+            Trace.instant(Trace.TRACE_TAG_APP,
+                    TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
+        }
     }
 
     private void notifyCujEvent(String action) {
diff --git a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
index 6a61656..096d1cd 100644
--- a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
+++ b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
@@ -137,14 +137,14 @@
         close();
     }
 
-    /** Records the start of ActivityManagerService#dumpStackTraces. */
+    /** Records the start of StackTracesDumpHelper#dumpStackTraces. */
     public void dumpStackTracesStarted() {
         mDumpStackTracesStartUptime = getUptimeMillis();
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
                 "dumpStackTraces()");
     }
 
-    /** Records the end of ActivityManagerService#dumpStackTraces. */
+    /** Records the end of StackTracesDumpHelper#dumpStackTraces. */
     public void dumpStackTracesEnded() {
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -328,7 +328,7 @@
         anrSkipped("appNotResponding");
     }
 
-    /** Records a skipped ANR in ActivityManagerService#dumpStackTraces. */
+    /** Records a skipped ANR in StackTracesDumpHelper#dumpStackTraces. */
     public void anrSkippedDumpStackTraces() {
         anrSkipped("dumpStackTraces");
     }
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 8f04cda..c1dbc87 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -111,6 +111,7 @@
     void clickTile(in ComponentName tile);
     @UnsupportedAppUsage
     void handleSystemKey(in int key);
+    int getLastSystemKey();
 
     /**
      * Methods to show toast messages for screen pinning
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index a0f7905..f08e860 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -5,12 +5,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StyleRes;
+import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.text.TextFlags;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -46,8 +48,6 @@
  */
 final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKeyListener,
         PopupWindow.OnDismissListener {
-    private static final int ITEM_LAYOUT = com.android.internal.R.layout.cascading_menu_item_layout;
-
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT})
     public @interface HorizPosition {}
@@ -190,6 +190,7 @@
     private Callback mPresenterCallback;
     private ViewTreeObserver mTreeObserver;
     private PopupWindow.OnDismissListener mOnDismissListener;
+    private final int mItemLayout;
 
     /** Whether popup menus should disable exit animations when closing. */
     private boolean mShouldCloseImmediately;
@@ -215,6 +216,12 @@
                 res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
 
         mSubMenuHoverHandler = new Handler();
+
+        mItemLayout = AppGlobals.getIntCoreSetting(
+                TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU,
+                TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT ? 1 : 0) != 0
+                ? com.android.internal.R.layout.cascading_menu_item_layout_material
+                : com.android.internal.R.layout.cascading_menu_item_layout;
     }
 
     @Override
@@ -348,7 +355,7 @@
      */
     private void showMenu(@NonNull MenuBuilder menu) {
         final LayoutInflater inflater = LayoutInflater.from(mContext);
-        final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly, ITEM_LAYOUT);
+        final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly, mItemLayout);
 
         // Apply "force show icon" setting. There are 3 cases:
         // (1) This is the top level menu and icon spacing is forced. Add spacing.
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 0d2b29b..cb1abf1 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.view.menu;
 
-import com.android.internal.R;
-
+import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.TextFlags;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -61,6 +61,8 @@
 
     private int mMenuType;
 
+    private boolean mUseNewContextMenu;
+
     private LayoutInflater mInflater;
 
     private boolean mForceShowIcon;
@@ -87,6 +89,10 @@
 
         a.recycle();
         b.recycle();
+
+        mUseNewContextMenu = AppGlobals.getIntCoreSetting(
+                TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU,
+                TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT ? 1 : 0) != 0;
     }
 
     public ListMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -283,7 +289,9 @@
 
     private void insertIconView() {
         LayoutInflater inflater = getInflater();
-        mIconView = (ImageView) inflater.inflate(com.android.internal.R.layout.list_menu_item_icon,
+        mIconView = (ImageView) inflater.inflate(
+                mUseNewContextMenu ? com.android.internal.R.layout.list_menu_item_fixed_size_icon :
+                        com.android.internal.R.layout.list_menu_item_icon,
                 this, false);
         addContentView(mIconView, 0);
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 12106c7..2f0ef14 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7401,6 +7401,8 @@
          <p>Protection level: normal
     -->
     <permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"
+                android:label="@string/permlab_updatePackagesWithoutUserAction"
+                android:description="@string/permdesc_updatePackagesWithoutUserAction"
                 android:protectionLevel="normal" />
     <uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"/>
 
diff --git a/core/res/res/layout/cascading_menu_item_layout_material.xml b/core/res/res/layout/cascading_menu_item_layout_material.xml
new file mode 100644
index 0000000..168ed78
--- /dev/null
+++ b/core/res/res/layout/cascading_menu_item_layout_material.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Keep in sync with popup_menu_item_layout.xml (which only differs in the title and shortcut
+    position). -->
+<com.android.internal.view.menu.ListMenuItemView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minWidth="112dip"
+    android:maxWidth="280dip"
+    android:orientation="vertical" >
+
+    <ImageView
+        android:id="@+id/group_divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dip"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip"
+        android:background="@drawable/list_divider_material" />
+
+    <LinearLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="48dip"
+        android:layout_marginStart="12dip"
+        android:layout_marginEnd="12dip"
+        android:duplicateParentState="true" >
+
+        <!-- Icon will be inserted here. -->
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="6dip"
+            android:textAppearance="?attr/textAppearanceLargePopupMenu"
+            android:singleLine="true"
+            android:duplicateParentState="true"
+            android:textAlignment="viewStart" />
+
+        <Space
+            android:layout_width="0dip"
+            android:layout_height="1dip"
+            android:layout_weight="1"/>
+
+        <TextView
+            android:id="@+id/shortcut"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="16dip"
+            android:textAppearance="?attr/textAppearanceSmallPopupMenu"
+            android:singleLine="true"
+            android:duplicateParentState="true"
+            android:textAlignment="viewEnd" />
+
+        <ImageView
+            android:id="@+id/submenuarrow"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginStart="8dp"
+            android:scaleType="center"
+            android:visibility="gone" />
+
+        <!-- Checkbox, and/or radio button will be inserted here. -->
+
+    </LinearLayout>
+
+</com.android.internal.view.menu.ListMenuItemView>
diff --git a/core/res/res/layout/list_menu_item_fixed_size_icon.xml b/core/res/res/layout/list_menu_item_fixed_size_icon.xml
new file mode 100644
index 0000000..7797682
--- /dev/null
+++ b/core/res/res/layout/list_menu_item_fixed_size_icon.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/icon"
+    android:layout_width="24dp"
+    android:layout_height="24dp"
+    android:layout_gravity="center_vertical"
+    android:layout_marginStart="0dip"
+    android:layout_marginEnd="6dip"
+    android:layout_marginTop="0dip"
+    android:layout_marginBottom="0dip"
+    android:scaleType="centerInside"
+    android:duplicateParentState="true" />
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6afdae5..3ee8af2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2191,6 +2191,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
     <string name="permdesc_highSamplingRateSensors">Allows the app to sample sensor data at a rate greater than 200 Hz</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permlab_updatePackagesWithoutUserAction">update app without user action</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
+    <string name="permdesc_updatePackagesWithoutUserAction">Allows the holder to update the app it previously installed without user action</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8fb7c9d..5c734df 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1476,6 +1476,7 @@
   <java-symbol type="layout" name="action_mode_close_item" />
   <java-symbol type="layout" name="alert_dialog" />
   <java-symbol type="layout" name="cascading_menu_item_layout" />
+  <java-symbol type="layout" name="cascading_menu_item_layout_material" />
   <java-symbol type="layout" name="choose_account" />
   <java-symbol type="layout" name="choose_account_row" />
   <java-symbol type="layout" name="choose_account_type" />
@@ -1525,6 +1526,7 @@
   <java-symbol type="layout" name="list_content_simple" />
   <java-symbol type="layout" name="list_menu_item_checkbox" />
   <java-symbol type="layout" name="list_menu_item_icon" />
+  <java-symbol type="layout" name="list_menu_item_fixed_size_icon" />
   <java-symbol type="layout" name="list_menu_item_layout" />
   <java-symbol type="layout" name="list_menu_item_radio" />
   <java-symbol type="layout" name="locale_picker_item" />
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 404429a..89f4890 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -40,7 +40,9 @@
 import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
 import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
 import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
+import static androidx.window.extensions.embedding.SplitPresenter.getActivitiesMinDimensionsPair;
 import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair;
+import static androidx.window.extensions.embedding.SplitPresenter.getTaskWindowMetrics;
 import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
 
 import android.app.Activity;
@@ -1037,9 +1039,15 @@
         final TaskFragmentContainer primaryContainer = getContainerWithActivity(
                 primaryActivity);
         final SplitContainer splitContainer = getActiveSplitForContainer(primaryContainer);
-        final WindowMetrics taskWindowMetrics = mPresenter.getTaskWindowMetrics(primaryActivity);
+        final TaskContainer.TaskProperties taskProperties = mPresenter
+                .getTaskProperties(primaryActivity);
+        final SplitAttributes calculatedSplitAttributes = mPresenter.computeSplitAttributes(
+                taskProperties, splitRule, splitRule.getDefaultSplitAttributes(),
+                getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity));
         if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer()
-                && canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics)) {
+                && canReuseContainer(splitRule, splitContainer.getSplitRule(),
+                        getTaskWindowMetrics(taskProperties.getConfiguration()),
+                        calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
             // Can launch in the existing secondary container if the rules share the same
             // presentation.
             final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
@@ -1058,7 +1066,8 @@
             }
         }
         // Create new split pair.
-        mPresenter.createNewSplitContainer(wct, primaryActivity, secondaryActivity, splitRule);
+        mPresenter.createNewSplitContainer(wct, primaryActivity, secondaryActivity, splitRule,
+                calculatedSplitAttributes);
         return true;
     }
 
@@ -1283,9 +1292,16 @@
         }
         final TaskFragmentContainer existingContainer = getContainerWithActivity(primaryActivity);
         final SplitContainer splitContainer = getActiveSplitForContainer(existingContainer);
-        final WindowMetrics taskWindowMetrics = mPresenter.getTaskWindowMetrics(primaryActivity);
+        final TaskContainer.TaskProperties taskProperties = mPresenter
+                .getTaskProperties(primaryActivity);
+        final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(
+                taskProperties.getConfiguration());
+        final SplitAttributes calculatedSplitAttributes = mPresenter.computeSplitAttributes(
+                taskProperties, splitRule, splitRule.getDefaultSplitAttributes(),
+                getActivityIntentMinDimensionsPair(primaryActivity, intent));
         if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer()
-                && (canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics)
+                && (canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics,
+                        calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())
                 // TODO(b/231845476) we should always respect clearTop.
                 || !respectClearTop)
                 && mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
@@ -1296,7 +1312,7 @@
         }
         // Create a new TaskFragment to split with the primary activity for the new activity.
         return mPresenter.createNewSplitWithEmptySideContainer(wct, primaryActivity, intent,
-                splitRule);
+                splitRule, calculatedSplitAttributes);
     }
 
     /**
@@ -2273,21 +2289,29 @@
     }
 
     /**
-     * If the two rules have the same presentation, we can reuse the same {@link SplitContainer} if
-     * there is any.
+     * If the two rules have the same presentation, and the calculated {@link SplitAttributes}
+     * matches the {@link SplitAttributes} of {@link SplitContainer}, we can reuse the same
+     * {@link SplitContainer} if there is any.
      */
     private static boolean canReuseContainer(@NonNull SplitRule rule1, @NonNull SplitRule rule2,
-            @NonNull WindowMetrics parentWindowMetrics) {
+            @NonNull WindowMetrics parentWindowMetrics,
+            @NonNull SplitAttributes calculatedSplitAttributes,
+            @NonNull SplitAttributes containerSplitAttributes) {
         if (!isContainerReusableRule(rule1) || !isContainerReusableRule(rule2)) {
             return false;
         }
-        return haveSamePresentation((SplitPairRule) rule1, (SplitPairRule) rule2,
-                parentWindowMetrics);
+        return areRulesSamePresentation((SplitPairRule) rule1, (SplitPairRule) rule2,
+                parentWindowMetrics)
+                // Besides rules, we should also check whether the SplitContainer's splitAttributes
+                // matches the current splitAttributes or not. The splitAttributes may change
+                // if the app chooses different SplitAttributes calculator function before a new
+                // activity is started even they match the same splitRule.
+                && calculatedSplitAttributes.equals(containerSplitAttributes);
     }
 
     /** Whether the two rules have the same presentation. */
     @VisibleForTesting
-    static boolean haveSamePresentation(@NonNull SplitPairRule rule1,
+    static boolean areRulesSamePresentation(@NonNull SplitPairRule rule1,
             @NonNull SplitPairRule rule2, @NonNull WindowMetrics parentWindowMetrics) {
         if (rule1.getTag() != null || rule2.getTag() != null) {
             // Tag must be unique if it is set. We don't want to reuse the container if the rules
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 0408511..20b6ae2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -174,12 +174,9 @@
     @NonNull
     TaskFragmentContainer createNewSplitWithEmptySideContainer(
             @NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity,
-            @NonNull Intent secondaryIntent, @NonNull SplitPairRule rule) {
+            @NonNull Intent secondaryIntent, @NonNull SplitPairRule rule,
+            @NonNull SplitAttributes splitAttributes) {
         final TaskProperties taskProperties = getTaskProperties(primaryActivity);
-        final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(
-                primaryActivity, secondaryIntent);
-        final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
-                rule.getDefaultSplitAttributes(), minDimensionsPair);
         final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
                 splitAttributes);
         final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
@@ -217,15 +214,12 @@
      *                          same container as the primary activity, a new container will be
      *                          created and the activity will be re-parented to it.
      * @param rule The split rule to be applied to the container.
+     * @param splitAttributes The {@link SplitAttributes} to apply
      */
     void createNewSplitContainer(@NonNull WindowContainerTransaction wct,
             @NonNull Activity primaryActivity, @NonNull Activity secondaryActivity,
-            @NonNull SplitPairRule rule) {
+            @NonNull SplitPairRule rule, @NonNull SplitAttributes splitAttributes) {
         final TaskProperties taskProperties = getTaskProperties(primaryActivity);
-        final Pair<Size, Size> minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity,
-                secondaryActivity);
-        final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
-                rule.getDefaultSplitAttributes(), minDimensionsPair);
         final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
                 splitAttributes);
         final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
@@ -654,7 +648,7 @@
     }
 
     @NonNull
-    private static Pair<Size, Size> getActivitiesMinDimensionsPair(
+    static Pair<Size, Size> getActivitiesMinDimensionsPair(
             @NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) {
         return new Pair<>(getMinDimensions(primaryActivity), getMinDimensions(secondaryActivity));
     }
@@ -1027,7 +1021,7 @@
     }
 
     @NonNull
-    private static WindowMetrics getTaskWindowMetrics(@NonNull Configuration taskConfiguration) {
+    static WindowMetrics getTaskWindowMetrics(@NonNull Configuration taskConfiguration) {
         final Rect taskBounds = taskConfiguration.windowConfiguration.getBounds();
         // TODO(b/190433398): Supply correct insets.
         final float density = taskConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index cbdc262..ff08782 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -565,7 +565,6 @@
         assertNotNull(mSplitController.getActiveSplitForContainers(primaryContainer, container));
         assertTrue(primaryContainer.areLastRequestedBoundsEqual(null));
         assertTrue(container.areLastRequestedBoundsEqual(null));
-        assertEquals(container, mSplitController.getContainerWithActivity(secondaryActivity));
     }
 
     @Test
@@ -1008,9 +1007,8 @@
 
         assertTrue(result);
         assertSplitPair(primaryActivity, mActivity, true /* matchParentBounds */);
-        assertEquals(mSplitController.getContainerWithActivity(secondaryActivity),
-                mSplitController.getContainerWithActivity(mActivity));
-        verify(mSplitPresenter, never()).createNewSplitContainer(any(), any(), any(), any());
+        assertTrue(mSplitController.getContainerWithActivity(mActivity)
+                .areLastRequestedBoundsEqual(new Rect()));
     }
 
     @Test
@@ -1215,7 +1213,7 @@
                 .build();
 
         assertTrue("Rules must have same presentation if tags are null and has same properties.",
-                SplitController.haveSamePresentation(splitRule1, splitRule2,
+                SplitController.areRulesSamePresentation(splitRule1, splitRule2,
                         new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
 
         splitRule2 = createSplitPairRuleBuilder(
@@ -1230,7 +1228,7 @@
 
         assertFalse("Rules must have different presentations if tags are not equal regardless"
                         + "of other properties",
-                SplitController.haveSamePresentation(splitRule1, splitRule2,
+                SplitController.areRulesSamePresentation(splitRule1, splitRule2,
                         new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
     }
 
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 8330156..8dd1384 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -678,7 +678,8 @@
                 .setShouldClearTop(false)
                 .build();
 
-        mPresenter.createNewSplitContainer(mTransaction, mActivity, secondaryActivity, rule);
+        mPresenter.createNewSplitContainer(mTransaction, mActivity, secondaryActivity, rule,
+                SPLIT_ATTRIBUTES);
 
         assertEquals(primaryTf, mController.getContainerWithActivity(mActivity));
         final TaskFragmentContainer secondaryTf = mController.getContainerWithActivity(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
index ae33b94..4eaedd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
@@ -25,7 +25,10 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.FloatProperty;
@@ -34,6 +37,7 @@
 import android.view.IRemoteAnimationRunner;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
+import android.view.WindowManager.LayoutParams;
 import android.view.animation.Animation;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Transformation;
@@ -43,6 +47,7 @@
 import android.window.BackProgressAnimator;
 import android.window.IOnBackInvokedCallback;
 
+import com.android.internal.R;
 import com.android.internal.dynamicanimation.animation.SpringAnimation;
 import com.android.internal.dynamicanimation.animation.SpringForce;
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -78,6 +83,7 @@
     final CustomAnimationLoader mCustomAnimationLoader;
     private Animation mEnterAnimation;
     private Animation mCloseAnimation;
+    private int mNextBackgroundColor;
     final Transformation mTransformation = new Transformation();
 
     private final Choreographer mChoreographer;
@@ -144,8 +150,9 @@
 
         // Draw background with task background color.
         if (mEnteringTarget.taskInfo != null && mEnteringTarget.taskInfo.taskDescription != null) {
-            mBackground.ensureBackground(
-                    mEnteringTarget.taskInfo.taskDescription.getBackgroundColor(), mTransaction);
+            mBackground.ensureBackground(mNextBackgroundColor == Color.TRANSPARENT
+                    ? mEnteringTarget.taskInfo.taskDescription.getBackgroundColor()
+                    : mNextBackgroundColor, mTransaction);
         }
     }
 
@@ -191,6 +198,7 @@
         mTransaction.apply();
         mTransformation.clear();
         mLatestProgress = 0;
+        mNextBackgroundColor = Color.TRANSPARENT;
         if (mFinishCallback != null) {
             try {
                 mFinishCallback.onAnimationFinished();
@@ -252,11 +260,11 @@
      * Load customize animation before animation start.
      */
     boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) {
-        mCloseAnimation = mCustomAnimationLoader.load(
-                animationInfo, false /* enterAnimation */);
-        if (mCloseAnimation != null) {
-            mEnterAnimation = mCustomAnimationLoader.load(
-                    animationInfo, true /* enterAnimation */);
+        final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo);
+        if (result != null) {
+            mCloseAnimation = result.mCloseAnimation;
+            mEnterAnimation = result.mEnterAnimation;
+            mNextBackgroundColor = result.mBackgroundColor;
             return true;
         }
         return false;
@@ -318,35 +326,79 @@
         }
     }
 
+
+    static final class AnimationLoadResult {
+        Animation mCloseAnimation;
+        Animation mEnterAnimation;
+        int mBackgroundColor;
+    }
+
     /**
      * Helper class to load custom animation.
      */
     static class CustomAnimationLoader {
-        private final TransitionAnimation mTransitionAnimation;
+        final TransitionAnimation mTransitionAnimation;
 
         CustomAnimationLoader(Context context) {
             mTransitionAnimation = new TransitionAnimation(
                     context, false /* debug */, "CustomizeBackAnimation");
         }
 
-        Animation load(BackNavigationInfo.CustomAnimationInfo animationInfo,
+        /**
+         * Load both enter and exit animation for the close activity transition.
+         * Note that the result is only valid if the exit animation has set and loaded success.
+         * If the entering animation has not set(i.e. 0), here will load the default entering
+         * animation for it.
+         *
+         * @param animationInfo The information of customize animation, which can be set from
+         * {@link Activity#overrideActivityTransition} and/or
+         * {@link LayoutParams#windowAnimations}
+         */
+        AnimationLoadResult loadAll(BackNavigationInfo.CustomAnimationInfo animationInfo) {
+            if (animationInfo.getPackageName().isEmpty()) {
+                return null;
+            }
+            final Animation close = loadAnimation(animationInfo, false);
+            if (close == null) {
+                return null;
+            }
+            final Animation open = loadAnimation(animationInfo, true);
+            AnimationLoadResult result = new AnimationLoadResult();
+            result.mCloseAnimation = close;
+            result.mEnterAnimation = open;
+            result.mBackgroundColor = animationInfo.getCustomBackground();
+            return result;
+        }
+
+        /**
+         * Load enter or exit animation from CustomAnimationInfo
+         * @param animationInfo The information for customize animation.
+         * @param enterAnimation true when load for enter animation, false for exit animation.
+         * @return Loaded animation.
+         */
+        @Nullable
+        Animation loadAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
                 boolean enterAnimation) {
-            final String packageName = animationInfo.getPackageName();
-            if (packageName.isEmpty()) {
-                return null;
+            Animation a = null;
+            // Activity#overrideActivityTransition has higher priority than windowAnimations
+            // Try to get animation from Activity#overrideActivityTransition
+            if ((enterAnimation && animationInfo.getCustomEnterAnim() != 0)
+                    || (!enterAnimation && animationInfo.getCustomExitAnim() != 0)) {
+                a = mTransitionAnimation.loadAppTransitionAnimation(
+                        animationInfo.getPackageName(),
+                        enterAnimation ? animationInfo.getCustomEnterAnim()
+                                : animationInfo.getCustomExitAnim());
+            } else if (animationInfo.getWindowAnimations() != 0) {
+                // try to get animation from LayoutParams#windowAnimations
+                a = mTransitionAnimation.loadAnimationAttr(animationInfo.getPackageName(),
+                        animationInfo.getWindowAnimations(), enterAnimation
+                                ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+                                : R.styleable.WindowAnimation_activityCloseExitAnimation,
+                        false /* translucent */);
             }
-            final int windowAnimations = animationInfo.getWindowAnimations();
-            if (windowAnimations == 0) {
-                return null;
-            }
-            final int attrs = enterAnimation
-                    ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
-                    : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
-            Animation a = mTransitionAnimation.loadAnimationAttr(packageName, windowAnimations,
-                    attrs, false /* translucent */);
             // Only allow to load default animation for opening target.
             if (a == null && enterAnimation) {
-                a = mTransitionAnimation.loadDefaultAnimationAttr(attrs, false /* translucent */);
+                a = loadDefaultOpenAnimation();
             }
             if (a != null) {
                 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a);
@@ -355,5 +407,11 @@
             }
             return a;
         }
+
+        private Animation loadDefaultOpenAnimation() {
+            return mTransitionAnimation.loadDefaultAnimationAttr(
+                    R.styleable.WindowAnimation_activityCloseEnterAnimation,
+                    false /* translucent */);
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index c5fc879..f2f30ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1176,20 +1176,7 @@
 
         final Rect newDestinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         if (newDestinationBounds.equals(currentDestinationBounds)) return;
-        if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
-            if (mWaitForFixedRotation) {
-                // The new destination bounds are in next rotation (DisplayLayout has been rotated
-                // in computeRotatedBounds). The animation runs in previous rotation so the end
-                // bounds need to be transformed.
-                final Rect displayBounds = mPipBoundsState.getDisplayBounds();
-                final Rect rotatedEndBounds = new Rect(newDestinationBounds);
-                rotateBounds(rotatedEndBounds, displayBounds, mNextRotation, mCurrentRotation);
-                animator.updateEndValue(rotatedEndBounds);
-            } else {
-                animator.updateEndValue(newDestinationBounds);
-            }
-        }
-        animator.setDestinationBounds(newDestinationBounds);
+        updateAnimatorBounds(newDestinationBounds);
         destinationBoundsOut.set(newDestinationBounds);
     }
 
@@ -1201,7 +1188,17 @@
                 mPipAnimationController.getCurrentAnimator();
         if (animator != null && animator.isRunning()) {
             if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
-                animator.updateEndValue(bounds);
+                if (mWaitForFixedRotation) {
+                    // The new destination bounds are in next rotation (DisplayLayout has been
+                    // rotated in computeRotatedBounds). The animation runs in previous rotation so
+                    // the end bounds need to be transformed.
+                    final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+                    final Rect rotatedEndBounds = new Rect(bounds);
+                    rotateBounds(rotatedEndBounds, displayBounds, mNextRotation, mCurrentRotation);
+                    animator.updateEndValue(rotatedEndBounds);
+                } else {
+                    animator.updateEndValue(bounds);
+                }
             }
             animator.setDestinationBounds(bounds);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index e632b56..d25318d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -228,7 +228,7 @@
         } else if ((mEndWidth > mStartWidth) == (mEndHeight > mStartHeight)
                 && (mEndWidth != mStartWidth || mEndHeight != mStartHeight)) {
             // Display resizes without rotation change.
-            final float scale = Math.max((float) mEndWidth / mStartHeight,
+            final float scale = Math.max((float) mEndWidth / mStartWidth,
                     (float) mEndHeight / mStartHeight);
             matrix.setScale(scale, scale);
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
index 2814ef9..e7d4598 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
@@ -18,14 +18,20 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.app.WindowConfiguration;
+import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
@@ -69,11 +75,7 @@
                 mBackAnimationBackground, mock(SurfaceControl.Transaction.class),
                 mock(Choreographer.class));
         spyOn(mCustomizeActivityAnimation);
-        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .load(any(), eq(false));
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .load(any(), eq(true));
+        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation);
     }
 
     RemoteAnimationTarget createAnimationTarget(boolean open) {
@@ -87,6 +89,12 @@
 
     @Test
     public void receiveFinishAfterInvoke() throws InterruptedException {
+        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
+        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
+                .loadAnimation(any(), eq(false));
+        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
+                .loadAnimation(any(), eq(true));
+
         mCustomizeActivityAnimation.prepareNextAnimation(
                 new BackNavigationInfo.CustomAnimationInfo("TestPackage"));
         final RemoteAnimationTarget close = createAnimationTarget(false);
@@ -112,6 +120,12 @@
 
     @Test
     public void receiveFinishAfterCancel() throws InterruptedException {
+        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
+        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
+                .loadAnimation(any(), eq(false));
+        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
+                .loadAnimation(any(), eq(true));
+
         mCustomizeActivityAnimation.prepareNextAnimation(
                 new BackNavigationInfo.CustomAnimationInfo("TestPackage"));
         final RemoteAnimationTarget close = createAnimationTarget(false);
@@ -152,4 +166,67 @@
         verify(mCustomizeActivityAnimation).onGestureCommitted();
         finishCalled.await(1, TimeUnit.SECONDS);
     }
+
+    @Test
+    public void testLoadCustomAnimation() {
+        testLoadCustomAnimation(10, 20, 0);
+    }
+
+    @Test
+    public void testLoadCustomAnimationNoEnter() {
+        testLoadCustomAnimation(0, 10, 0);
+    }
+
+    @Test
+    public void testLoadWindowAnimations() {
+        testLoadCustomAnimation(0, 0, 30);
+    }
+
+    @Test
+    public void testCustomAnimationHigherThanWindowAnimations() {
+        testLoadCustomAnimation(10, 20, 30);
+    }
+
+    private void testLoadCustomAnimation(int enterResId, int exitResId, int windowAnimations) {
+        final String testPackage = "TestPackage";
+        BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
+                .setCustomAnimation(testPackage, enterResId, exitResId, Color.GREEN)
+                .setWindowAnimations(testPackage, windowAnimations);
+        final BackNavigationInfo.CustomAnimationInfo info = builder.build()
+                .getCustomAnimationInfo();
+
+        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
+                        .mTransitionAnimation)
+                .loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
+        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
+                        .mTransitionAnimation)
+                .loadAppTransitionAnimation(eq(testPackage), eq(exitResId));
+        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
+                        .mTransitionAnimation)
+                .loadAnimationAttr(eq(testPackage), eq(windowAnimations), anyInt(), anyBoolean());
+        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
+                        .mTransitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean());
+
+        CustomizeActivityAnimation.AnimationLoadResult result =
+                mCustomizeActivityAnimation.mCustomAnimationLoader.loadAll(info);
+
+        if (exitResId != 0) {
+            if (enterResId == 0) {
+                verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
+                        never()).loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
+                verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation)
+                        .loadDefaultAnimationAttr(anyInt(), anyBoolean());
+            } else {
+                assertEquals(result.mEnterAnimation, mMockOpenAnimation);
+            }
+            assertEquals(result.mBackgroundColor, Color.GREEN);
+            assertEquals(result.mCloseAnimation, mMockCloseAnimation);
+            verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never())
+                    .loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
+        } else if (windowAnimations != 0) {
+            verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
+                    times(2)).loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
+            assertEquals(result.mCloseAnimation, mMockCloseAnimation);
+        }
+    }
 }
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 1d069b6..1e0c2cd 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -103,6 +103,8 @@
   <string name="get_dialog_title_use_sign_in_for">Use your saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
   <!-- This appears as the title of the dialog asking for user to make a choice from various previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
   <string name="get_dialog_title_choose_sign_in_for">Choose a saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+  <!-- This appears as the title of the dialog asking for user to make a choice from various previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
+  <string name="get_dialog_title_choose_option_for">Choose an option for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
   <!-- This is a label for a button that links the user to different sign-in methods . [CHAR LIMIT=80] -->
   <string name="get_dialog_use_saved_passkey_for">Sign in another way</string>
   <!-- This is a label for a button that links the user to different sign-in methods. [CHAR LIMIT=80] -->
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt
index 8b95b5e..ba48f2b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt
@@ -19,8 +19,31 @@
 import androidx.activity.compose.ManagedActivityResultLauncher
 import androidx.activity.result.ActivityResult
 import androidx.activity.result.IntentSenderRequest
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.Divider
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.core.graphics.drawable.toBitmap
+import com.android.compose.rememberSystemUiController
 import com.android.credentialmanager.CredentialSelectorViewModel
+import com.android.credentialmanager.R
+import com.android.credentialmanager.common.BaseEntry
+import com.android.credentialmanager.common.ProviderActivityState
+import com.android.credentialmanager.common.ui.CredentialContainerCard
+import com.android.credentialmanager.common.ui.HeadlineIcon
+import com.android.credentialmanager.common.ui.HeadlineText
+import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
+import com.android.credentialmanager.common.ui.ModalBottomSheet
+import com.android.credentialmanager.common.ui.SheetContainerCard
+import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor
+import com.android.credentialmanager.logging.GetCredentialEvent
+import com.android.internal.logging.UiEventLogger
+
 
 @Composable
 fun GetGenericCredentialScreen(
@@ -28,5 +51,102 @@
         getCredentialUiState: GetCredentialUiState,
         providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
 ) {
-    // TODO(b/274129098): Implement Screen for mDocs
-}
\ No newline at end of file
+    val sysUiController = rememberSystemUiController()
+    setBottomSheetSystemBarsColor(sysUiController)
+    ModalBottomSheet(
+        sheetContent = {
+            when (viewModel.uiState.providerActivityState) {
+                ProviderActivityState.NOT_APPLICABLE -> {
+                    PrimarySelectionCardGeneric(
+                            requestDisplayInfo = getCredentialUiState.requestDisplayInfo,
+                            providerDisplayInfo = getCredentialUiState.providerDisplayInfo,
+                            providerInfoList = getCredentialUiState.providerInfoList,
+                            onEntrySelected = viewModel::getFlowOnEntrySelected,
+                            onLog = { viewModel.logUiEvent(it) },
+                    )
+                    viewModel.uiMetrics.log(GetCredentialEvent
+                            .CREDMAN_GET_CRED_SCREEN_PRIMARY_SELECTION)
+                }
+                ProviderActivityState.READY_TO_LAUNCH -> {
+                    // Launch only once per providerActivityState change so that the provider
+                    // UI will not be accidentally launched twice.
+                    LaunchedEffect(viewModel.uiState.providerActivityState) {
+                        viewModel.launchProviderUi(providerActivityLauncher)
+                    }
+                    viewModel.uiMetrics.log(GetCredentialEvent
+                            .CREDMAN_GET_CRED_PROVIDER_ACTIVITY_READY_TO_LAUNCH)
+                }
+                ProviderActivityState.PENDING -> {
+                    // Hide our content when the provider activity is active.
+                    viewModel.uiMetrics.log(GetCredentialEvent
+                            .CREDMAN_GET_CRED_PROVIDER_ACTIVITY_PENDING)
+                }
+            }
+        },
+        onDismiss = viewModel::onUserCancel,
+    )
+}
+
+@Composable
+fun PrimarySelectionCardGeneric(
+        requestDisplayInfo: RequestDisplayInfo,
+        providerDisplayInfo: ProviderDisplayInfo,
+        providerInfoList: List<ProviderInfo>,
+        onEntrySelected: (BaseEntry) -> Unit,
+        onLog: @Composable (UiEventLogger.UiEventEnum) -> Unit,
+) {
+    val sortedUserNameToCredentialEntryList =
+            providerDisplayInfo.sortedUserNameToCredentialEntryList
+    SheetContainerCard {
+        // When only one provider (not counting the remote-only provider) exists, display that
+        // provider's icon + name up top.
+        if (providerInfoList.size <= 2) { // It's only possible to be the single provider case
+            // if we are started with no more than 2 providers.
+            val nonRemoteProviderList = providerInfoList.filter(
+                { it.credentialEntryList.isNotEmpty() || it.authenticationEntryList.isNotEmpty() }
+            )
+            if (nonRemoteProviderList.size == 1) {
+                val providerInfo = nonRemoteProviderList.firstOrNull() // First should always work
+                // but just to be safe.
+                if (providerInfo != null) {
+                    item {
+                        HeadlineIcon(
+                                bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
+                                tint = Color.Unspecified,
+                        )
+                    }
+                    item { Divider(thickness = 4.dp, color = Color.Transparent) }
+                    item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) }
+                    item { Divider(thickness = 16.dp, color = Color.Transparent) }
+                }
+            }
+        }
+
+        item {
+            HeadlineText(
+                    text = stringResource(
+                            R.string.get_dialog_title_choose_option_for,
+                            requestDisplayInfo.appName
+                    ),
+            )
+        }
+        item { Divider(thickness = 24.dp, color = Color.Transparent) }
+        item {
+            CredentialContainerCard {
+                Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
+                    // Show max 4 entries in this primary page
+                    sortedUserNameToCredentialEntryList.forEach {
+                        // TODO(b/275375861): fallback UI merges entries by account names.
+                        //  Need a strategy to be able to show all entries.
+                        CredentialEntryRow(
+                                credentialEntryInfo = it.sortedCredentialEntryList.first(),
+                                onEntrySelected = onEntrySelected,
+                                enforceOneLine = true,
+                        )
+                    }
+                }
+            }
+        }
+    }
+    onLog(GetCredentialEvent.CREDMAN_GET_CRED_PRIMARY_SELECTION_CARD)
+}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/README.md b/packages/SystemUI/accessibility/accessibilitymenu/README.md
new file mode 100644
index 0000000..b7fc363
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/README.md
@@ -0,0 +1,40 @@
+The Accessibility Menu is an accessibility service
+that presents a large on-screen menu to control your Android device.
+This service can be enabled from the Accessibility page in the Settings app.
+You can control gestures, hardware buttons, navigation, and more. From the menu, you can:
+
+- Take screenshots
+- Lock your screen
+- Open the device's voice assistant
+- Open Quick Settings and Notifications
+- Turn volume up or down
+- Turn brightness up or down
+
+The UI consists of a `ViewPager` populated by multiple pages of shortcut buttons.
+In the settings for the menu, there is an option to display the buttons in a 3x3 grid per page,
+or a 2x2 grid with larger buttons.
+
+Upon activation, most buttons will close the menu while performing their function.
+The exception to this are buttons that adjust a value, like volume or brightness,
+where the user is likely to want to press the button multiple times.
+In addition, touching other parts of the screen or locking the phone through other means
+should dismiss the menu.
+
+A majority of the shortcuts correspond directly to an existing accessibility service global action
+(see `AccessibilityService#performGlobalAction()` constants) that is performed when pressed.
+Shortcuts that navigate to a different menu, such as Quick Settings, use an intent to do so.
+Shortcuts that adjust brightness or volume interface directly with
+`DisplayManager` & `AudioManager` respectively.
+
+To add a new shortcut:
+
+1. Add a value for the new shortcut to the `ShortcutId` enum in `A11yMenuShortcut`.
+2. Put an entry for the enum value into the `sShortcutResource` `HashMap` in `A11yMenuShortcut`.
+This will require resources for a drawable icon, a color for the icon,
+the displayed name of the shortcut and the desired text-to-speech output.
+3. Add the enum value to the `SHORTCUT_LIST_DEFAULT` & `LARGE_SHORTCUT_LIST_DEFAULT` arrays
+in `A11yMenuOverlayLayout`.
+4. For functionality, add a code block to the if-else chain in
+`AccessibilityMenuService.handleClick()`, detailing the effect of the shortcut.
+If you don't want the shortcut to close the menu,
+include a return statement at the end of the code block.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
index 4e70455..59911b2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
@@ -53,10 +53,6 @@
     /** Returns true if the gesture should be rejected. */
     boolean isFalseGesture();
 
-    public boolean swipedFarEnough(float translation, float viewSize);
-
-    public boolean swipedFastEnough(float translation, float velocity);
-
     @ProvidesInterface(version = SnoozeOption.VERSION)
     public interface SnoozeOption {
         public static final int VERSION = 2;
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 2871cdf..4048a39 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -64,7 +64,8 @@
         android:layout_height="@dimen/keyguard_affordance_fixed_height"
         android:layout_width="@dimen/keyguard_affordance_fixed_width"
         android:layout_gravity="bottom|start"
-        android:scaleType="center"
+        android:scaleType="fitCenter"
+        android:padding="@dimen/keyguard_affordance_fixed_padding"
         android:tint="?android:attr/textColorPrimary"
         android:background="@drawable/keyguard_bottom_affordance_bg"
         android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
@@ -77,7 +78,8 @@
         android:layout_height="@dimen/keyguard_affordance_fixed_height"
         android:layout_width="@dimen/keyguard_affordance_fixed_width"
         android:layout_gravity="bottom|end"
-        android:scaleType="center"
+        android:scaleType="fitCenter"
+        android:padding="@dimen/keyguard_affordance_fixed_padding"
         android:tint="?android:attr/textColorPrimary"
         android:background="@drawable/keyguard_bottom_affordance_bg"
         android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 249fc86..0c0defa 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -793,6 +793,7 @@
     <dimen name="keyguard_affordance_fixed_height">48dp</dimen>
     <dimen name="keyguard_affordance_fixed_width">48dp</dimen>
     <dimen name="keyguard_affordance_fixed_radius">24dp</dimen>
+    <dimen name="keyguard_affordance_fixed_padding">12dp</dimen>
 
     <!-- Amount the button should shake when it's not long-pressed for long enough. -->
     <dimen name="keyguard_affordance_shake_amplitude">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 64a9cc9..2503520 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -17,7 +17,6 @@
 package com.android.systemui;
 
 import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_X;
-import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_Y;
 import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
 
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
@@ -92,7 +91,6 @@
     private float mTouchSlopMultiplier;
 
     private final Callback mCallback;
-    private final int mSwipeDirection;
     private final VelocityTracker mVelocityTracker;
     private final FalsingManager mFalsingManager;
     private final FeatureFlags mFeatureFlags;
@@ -141,12 +139,10 @@
     private final ArrayMap<View, Animator> mDismissPendingMap = new ArrayMap<>();
 
     public SwipeHelper(
-            int swipeDirection, Callback callback, Resources resources,
-            ViewConfiguration viewConfiguration, FalsingManager falsingManager,
-            FeatureFlags featureFlags) {
+            Callback callback, Resources resources, ViewConfiguration viewConfiguration,
+            FalsingManager falsingManager, FeatureFlags featureFlags) {
         mCallback = callback;
         mHandler = new Handler();
-        mSwipeDirection = swipeDirection;
         mVelocityTracker = VelocityTracker.obtain();
         mPagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
         mSlopMultiplier = viewConfiguration.getScaledAmbiguousGestureMultiplier();
@@ -179,22 +175,22 @@
     }
 
     private float getPos(MotionEvent ev) {
-        return mSwipeDirection == X ? ev.getX() : ev.getY();
+        return ev.getX();
     }
 
     private float getPerpendicularPos(MotionEvent ev) {
-        return mSwipeDirection == X ? ev.getY() : ev.getX();
+        return ev.getY();
     }
 
     protected float getTranslation(View v) {
-        return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
+        return v.getTranslationX();
     }
 
     private float getVelocity(VelocityTracker vt) {
-        return mSwipeDirection == X ? vt.getXVelocity() :
-                vt.getYVelocity();
+        return vt.getXVelocity();
     }
 
+
     protected Animator getViewTranslationAnimator(View view, float target,
             AnimatorUpdateListener listener) {
 
@@ -209,8 +205,7 @@
 
     protected Animator createTranslationAnimation(View view, float newPos,
             AnimatorUpdateListener listener) {
-        ObjectAnimator anim = ObjectAnimator.ofFloat(view,
-                mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
+        ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, newPos);
 
         if (listener != null) {
             anim.addUpdateListener(listener);
@@ -220,18 +215,13 @@
     }
 
     protected void setTranslation(View v, float translate) {
-        if (v == null) {
-            return;
-        }
-        if (mSwipeDirection == X) {
+        if (v != null) {
             v.setTranslationX(translate);
-        } else {
-            v.setTranslationY(translate);
         }
     }
 
     protected float getSize(View v) {
-        return mSwipeDirection == X ? v.getMeasuredWidth() : v.getMeasuredHeight();
+        return v.getMeasuredWidth();
     }
 
     public void setMinSwipeProgress(float minSwipeProgress) {
@@ -426,15 +416,12 @@
         float newPos;
         boolean isLayoutRtl = animView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
 
-        // if we use the Menu to dismiss an item in landscape, animate up
-        boolean animateUpForMenu = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
-                && mSwipeDirection == Y;
         // if the language is rtl we prefer swiping to the left
         boolean animateLeftForRtl = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
                 && isLayoutRtl;
         boolean animateLeft = (Math.abs(velocity) > getEscapeVelocity() && velocity < 0) ||
                 (getTranslation(animView) < 0 && !isDismissAll);
-        if (animateLeft || animateLeftForRtl || animateUpForMenu) {
+        if (animateLeft || animateLeftForRtl) {
             newPos = -getTotalTranslationLength(animView);
         } else {
             newPos = getTotalTranslationLength(animView);
@@ -576,8 +563,7 @@
                     startVelocity,
                     mSnapBackSpringConfig);
         }
-        return PhysicsAnimator.getInstance(target).spring(
-                mSwipeDirection == X ? TRANSLATION_X : TRANSLATION_Y, toPosition, startVelocity,
+        return PhysicsAnimator.getInstance(target).spring(TRANSLATION_X, toPosition, startVelocity,
                 mSnapBackSpringConfig);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 35819e3..9606bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -78,7 +78,7 @@
     private static final boolean DEBUG = true;
     private static final int HANDLE_BROADCAST_FAILED_DELAY = 3000;
 
-    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    protected final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
     private final RecyclerView.LayoutManager mLayoutManager;
 
     final Context mContext;
@@ -102,11 +102,13 @@
     private int mListMaxHeight;
     private int mItemHeight;
     private WallpaperColors mWallpaperColors;
-    private Executor mExecutor;
     private boolean mShouldLaunchLeBroadcastDialog;
+    private boolean mIsLeBroadcastCallbackRegistered;
 
     MediaOutputBaseAdapter mAdapter;
 
+    protected Executor mExecutor;
+
     private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
         ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams();
         int totalItemsHeight = mAdapter.getItemCount() * mItemHeight;
@@ -274,17 +276,19 @@
     public void onStart() {
         super.onStart();
         mMediaOutputController.start(this);
-        if(isBroadcastSupported()) {
-            mMediaOutputController.registerLeBroadcastServiceCallBack(mExecutor,
+        if (isBroadcastSupported() && !mIsLeBroadcastCallbackRegistered) {
+            mMediaOutputController.registerLeBroadcastServiceCallback(mExecutor,
                     mBroadcastCallback);
+            mIsLeBroadcastCallbackRegistered = true;
         }
     }
 
     @Override
     public void onStop() {
         super.onStop();
-        if(isBroadcastSupported()) {
-            mMediaOutputController.unregisterLeBroadcastServiceCallBack(mBroadcastCallback);
+        if (isBroadcastSupported() && mIsLeBroadcastCallbackRegistered) {
+            mMediaOutputController.unregisterLeBroadcastServiceCallback(mBroadcastCallback);
+            mIsLeBroadcastCallbackRegistered = false;
         }
         mMediaOutputController.stop();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index 12d6b7c..f0ff140 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -17,6 +17,10 @@
 package com.android.systemui.media.dialog;
 
 import android.app.AlertDialog;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Bundle;
@@ -34,8 +38,11 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.core.graphics.drawable.IconCompat;
 
+import com.android.settingslib.media.BluetoothMediaDevice;
+import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.qrcode.QrCodeGenerator;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastSender;
@@ -49,7 +56,7 @@
  */
 @SysUISingleton
 public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
-    private static final String TAG = "BroadcastDialog";
+    private static final String TAG = "MediaOutputBroadcastDialog";
 
     private ViewStub mBroadcastInfoArea;
     private ImageView mBroadcastQrCodeView;
@@ -66,6 +73,7 @@
     private String mCurrentBroadcastName;
     private String mCurrentBroadcastCode;
     private boolean mIsStopbyUpdateBroadcastCode = false;
+
     private TextWatcher mTextWatcher = new TextWatcher() {
         @Override
         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -105,6 +113,79 @@
         }
     };
 
+    private boolean mIsLeBroadcastAssistantCallbackRegistered;
+
+    private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
+            new BluetoothLeBroadcastAssistant.Callback() {
+                @Override
+                public void onSearchStarted(int reason) {
+                    Log.d(TAG, "Assistant-onSearchStarted: " + reason);
+                }
+
+                @Override
+                public void onSearchStartFailed(int reason) {
+                    Log.d(TAG, "Assistant-onSearchStartFailed: " + reason);
+                }
+
+                @Override
+                public void onSearchStopped(int reason) {
+                    Log.d(TAG, "Assistant-onSearchStopped: " + reason);
+                }
+
+                @Override
+                public void onSearchStopFailed(int reason) {
+                    Log.d(TAG, "Assistant-onSearchStopFailed: " + reason);
+                }
+
+                @Override
+                public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {
+                    Log.d(TAG, "Assistant-onSourceFound:");
+                }
+
+                @Override
+                public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+                    Log.d(TAG, "Assistant-onSourceAdded: Device: " + sink
+                            + ", sourceId: " + sourceId);
+                    mMainThreadHandler.post(() -> refreshUi());
+                }
+
+                @Override
+                public void onSourceAddFailed(@NonNull BluetoothDevice sink,
+                        @NonNull BluetoothLeBroadcastMetadata source, int reason) {
+                    Log.d(TAG, "Assistant-onSourceAddFailed: Device: " + sink);
+                }
+
+                @Override
+                public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId,
+                        int reason) {
+                    Log.d(TAG, "Assistant-onSourceModified:");
+                }
+
+                @Override
+                public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId,
+                        int reason) {
+                    Log.d(TAG, "Assistant-onSourceModifyFailed:");
+                }
+
+                @Override
+                public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId,
+                        int reason) {
+                    Log.d(TAG, "Assistant-onSourceRemoved:");
+                }
+
+                @Override
+                public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId,
+                        int reason) {
+                    Log.d(TAG, "Assistant-onSourceRemoveFailed:");
+                }
+
+                @Override
+                public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId,
+                        @NonNull BluetoothLeBroadcastReceiveState state) {
+                    Log.d(TAG, "Assistant-onReceiveStateChanged:");
+                }
+            };
+
     static final int METADATA_BROADCAST_NAME = 0;
     static final int METADATA_BROADCAST_CODE = 1;
 
@@ -131,6 +212,27 @@
     }
 
     @Override
+    public void onStart() {
+        super.onStart();
+        if (!mIsLeBroadcastAssistantCallbackRegistered) {
+            mIsLeBroadcastAssistantCallbackRegistered = true;
+            mMediaOutputController.registerLeBroadcastAssistantServiceCallback(mExecutor,
+                    mBroadcastAssistantCallback);
+        }
+        connectBroadcastWithActiveDevice();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mIsLeBroadcastAssistantCallbackRegistered) {
+            mIsLeBroadcastAssistantCallbackRegistered = false;
+            mMediaOutputController.unregisterLeBroadcastAssistantServiceCallback(
+                    mBroadcastAssistantCallback);
+        }
+    }
+
+    @Override
     int getHeaderIconRes() {
         return 0;
     }
@@ -224,6 +326,7 @@
         mCurrentBroadcastCode = getBroadcastMetadataInfo(METADATA_BROADCAST_CODE);
         mBroadcastName.setText(mCurrentBroadcastName);
         mBroadcastCode.setText(mCurrentBroadcastCode);
+        refresh(false);
     }
 
     private void inflateBroadcastInfoArea() {
@@ -233,7 +336,7 @@
 
     private void setQrCodeView() {
         //get the Metadata, and convert to BT QR code format.
-        String broadcastMetadata = getBroadcastMetadata();
+        String broadcastMetadata = getLocalBroadcastMetadataQrCodeString();
         if (broadcastMetadata.isEmpty()) {
             //TDOD(b/226708424) Error handling for unable to generate the QR code bitmap
             return;
@@ -249,6 +352,33 @@
         }
     }
 
+    void connectBroadcastWithActiveDevice() {
+        //get the Metadata, and convert to BT QR code format.
+        BluetoothLeBroadcastMetadata broadcastMetadata = getBroadcastMetadata();
+        if (broadcastMetadata == null) {
+            Log.e(TAG, "Error: There is no broadcastMetadata.");
+            return;
+        }
+        MediaDevice mediaDevice = mMediaOutputController.getCurrentConnectedMediaDevice();
+        if (mediaDevice == null || !(mediaDevice instanceof BluetoothMediaDevice)
+                || !mediaDevice.isBLEDevice()) {
+            Log.e(TAG, "Error: There is no active BT LE device.");
+            return;
+        }
+        BluetoothDevice sink = ((BluetoothMediaDevice) mediaDevice).getCachedDevice().getDevice();
+        Log.d(TAG, "The broadcastMetadata broadcastId: " + broadcastMetadata.getBroadcastId()
+                + ", the device: " + sink.getAnonymizedAddress());
+
+        if (mMediaOutputController.isThereAnyBroadcastSourceIntoSinkDevice(sink)) {
+            Log.d(TAG, "The sink device has the broadcast source now.");
+            return;
+        }
+        if (!mMediaOutputController.addSourceIntoSinkDeviceWithBluetoothLeAssistant(sink,
+                broadcastMetadata, /*isGroupOp=*/ true)) {
+            Log.e(TAG, "Error: Source add failed");
+        }
+    }
+
     private void updateBroadcastCodeVisibility() {
         mBroadcastCode.setTransformationMethod(
                 mIsPasswordHide ? HideReturnsTransformationMethod.getInstance()
@@ -282,7 +412,11 @@
         mAlertDialog.show();
     }
 
-    private String getBroadcastMetadata() {
+    private String getLocalBroadcastMetadataQrCodeString() {
+        return mMediaOutputController.getLocalBroadcastMetadataQrCodeString();
+    }
+
+    private BluetoothLeBroadcastMetadata getBroadcastMetadata() {
         return mMediaOutputController.getBroadcastMetadata();
     }
 
@@ -314,6 +448,17 @@
     }
 
     @Override
+    public boolean isBroadcastSupported() {
+        boolean isBluetoothLeDevice = false;
+        if (mMediaOutputController.getCurrentConnectedMediaDevice() != null) {
+            isBluetoothLeDevice = mMediaOutputController.isBluetoothLeDevice(
+                    mMediaOutputController.getCurrentConnectedMediaDevice());
+        }
+
+        return mMediaOutputController.isBroadcastSupported() && isBluetoothLeDevice;
+    }
+
+    @Override
     public void handleLeBroadcastStarted() {
         mRetryCount = 0;
         if (mAlertDialog != null) {
@@ -332,6 +477,7 @@
 
     @Override
     public void handleLeBroadcastMetadataChanged() {
+        Log.d(TAG, "handleLeBroadcastMetadataChanged:");
         refreshUi();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index f3f17d1..9ebc8e4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -25,7 +25,11 @@
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.WallpaperColors;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -66,6 +70,7 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastMetadata;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.media.InfoMediaManager;
@@ -1049,7 +1054,7 @@
                 ALLOWLIST_DURATION_MS);
     }
 
-    String getBroadcastMetadata() {
+    String getLocalBroadcastMetadataQrCodeString() {
         LocalBluetoothLeBroadcast broadcast =
                 mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
         if (broadcast == null) {
@@ -1061,6 +1066,17 @@
         return metadata != null ? metadata.convertToQrCodeString() : "";
     }
 
+    BluetoothLeBroadcastMetadata getBroadcastMetadata() {
+        LocalBluetoothLeBroadcast broadcast =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+        if (broadcast == null) {
+            Log.d(TAG, "getBroadcastMetadata: LE Audio Broadcast is null");
+            return null;
+        }
+
+        return broadcast.getLatestBluetoothLeBroadcastMetadata();
+    }
+
     boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
         final List<String> features = device.getFeatures();
         return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
@@ -1121,7 +1137,7 @@
         return true;
     }
 
-    void registerLeBroadcastServiceCallBack(
+    void registerLeBroadcastServiceCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BluetoothLeBroadcast.Callback callback) {
         LocalBluetoothLeBroadcast broadcast =
@@ -1130,10 +1146,11 @@
             Log.d(TAG, "The broadcast profile is null");
             return;
         }
+        Log.d(TAG, "Register LE broadcast callback");
         broadcast.registerServiceCallBack(executor, callback);
     }
 
-    void unregisterLeBroadcastServiceCallBack(
+    void unregisterLeBroadcastServiceCallback(
             @NonNull BluetoothLeBroadcast.Callback callback) {
         LocalBluetoothLeBroadcast broadcast =
                 mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
@@ -1141,9 +1158,59 @@
             Log.d(TAG, "The broadcast profile is null");
             return;
         }
+        Log.d(TAG, "Unregister LE broadcast callback");
         broadcast.unregisterServiceCallBack(callback);
     }
 
+    boolean isThereAnyBroadcastSourceIntoSinkDevice(BluetoothDevice sink) {
+        LocalBluetoothLeBroadcastAssistant assistant =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+        if (assistant == null) {
+            Log.d(TAG, "The broadcast assistant profile is null");
+            return false;
+        }
+        List<BluetoothLeBroadcastReceiveState> sourceList = assistant.getAllSources(sink);
+        Log.d(TAG, "isThereAnyBroadcastSourceIntoSinkDevice: List size: " + sourceList.size());
+        return !sourceList.isEmpty();
+    }
+
+    boolean addSourceIntoSinkDeviceWithBluetoothLeAssistant(BluetoothDevice sink,
+            BluetoothLeBroadcastMetadata metadata, boolean isGroupOp) {
+        LocalBluetoothLeBroadcastAssistant assistant =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+        if (assistant == null) {
+            Log.d(TAG, "The broadcast assistant profile is null");
+            return false;
+        }
+        assistant.addSource(sink, metadata, isGroupOp);
+        return true;
+    }
+
+    void registerLeBroadcastAssistantServiceCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
+        LocalBluetoothLeBroadcastAssistant assistant =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+        if (assistant == null) {
+            Log.d(TAG, "The broadcast assistant profile is null");
+            return;
+        }
+        Log.d(TAG, "Register LE broadcast assistant callback");
+        assistant.registerServiceCallBack(executor, callback);
+    }
+
+    void unregisterLeBroadcastAssistantServiceCallback(
+            @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
+        LocalBluetoothLeBroadcastAssistant assistant =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+        if (assistant == null) {
+            Log.d(TAG, "The broadcast assistant profile is null");
+            return;
+        }
+        Log.d(TAG, "Unregister LE broadcast assistant callback");
+        assistant.unregisterServiceCallBack(callback);
+    }
+
     private boolean isPlayBackInfoLocal() {
         return mMediaController != null
                 && mMediaController.getPlaybackInfo() != null
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
index 3a4ea3e..e352c61 100644
--- a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
@@ -182,6 +182,26 @@
                 interactionState = null
                 true
             }
+            MotionEvent.ACTION_POINTER_UP -> {
+                val removedPointerId = event.getPointerId(event.actionIndex)
+                if (removedPointerId == interactionState?.pointerId && event.pointerCount > 1) {
+                    // We removed the original pointer but there must be another pointer because the
+                    // gesture is still ongoing. Let's switch to that pointer.
+                    interactionState =
+                        event.firstUnremovedPointerId(removedPointerId)?.let { replacementPointerId
+                            ->
+                            interactionState?.copy(
+                                pointerId = replacementPointerId,
+                                // We want to update the currentY of our state so that the
+                                // transition to the next pointer doesn't report a big jump between
+                                // the Y coordinate of the removed pointer and the Y coordinate of
+                                // the replacement pointer.
+                                currentY = event.getY(replacementPointerId),
+                            )
+                        }
+                }
+                true
+            }
             MotionEvent.ACTION_CANCEL -> {
                 if (isDraggingShade()) {
                     // Our drag gesture was canceled by the system. This happens primarily in one of
@@ -219,4 +239,17 @@
     private fun isDraggingShade(): Boolean {
         return interactionState?.isDraggingShade ?: false
     }
+
+    /**
+     * Returns the index of the first pointer that is not [removedPointerId] or `null`, if there is
+     * no other pointer.
+     */
+    private fun MotionEvent.firstUnremovedPointerId(removedPointerId: Int): Int? {
+        return (0 until pointerCount)
+            .firstOrNull { pointerIndex ->
+                val pointerId = getPointerId(pointerIndex)
+                pointerId != removedPointerId
+            }
+            ?.let { pointerIndex -> getPointerId(pointerIndex) }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 0d5a3fd..a29eb3b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -56,7 +56,8 @@
 internal const val MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION = 300L
 private const val MIN_DURATION_ACTIVE_AFTER_INACTIVE_ANIMATION = 130L
 private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
-private const val MIN_DURATION_COMMITTED_ANIMATION = 120L
+private const val MIN_DURATION_COMMITTED_ANIMATION = 80L
+private const val MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION = 120L
 private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
 private const val MIN_DURATION_FLING_ANIMATION = 160L
 
@@ -918,7 +919,7 @@
                 if (previousState == GestureState.FLUNG) {
                     updateRestingArrowDimens()
                     mainHandler.postDelayed(onEndSetGoneStateListener.runnable,
-                            MIN_DURATION_COMMITTED_ANIMATION)
+                            MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION)
                 } else {
                     mView.popScale(POP_ON_FLING_SCALE)
                     mainHandler.postDelayed(onAlphaEndSetGoneStateListener.runnable,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index 35b6c15..6ce6f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -219,7 +219,7 @@
                         height = getDimen(R.dimen.navigation_edge_active_background_height),
                         edgeCornerRadius = getDimen(R.dimen.navigation_edge_active_edge_corners),
                         farCornerRadius = getDimen(R.dimen.navigation_edge_active_far_corners),
-                        widthSpring = createSpring(650f, 0.75f),
+                        widthSpring = createSpring(850f, 0.75f),
                         heightSpring = createSpring(10000f, 1f),
                         edgeCornerRadiusSpring = createSpring(600f, 0.36f),
                         farCornerRadiusSpring = createSpring(2500f, 0.855f),
@@ -274,8 +274,8 @@
                         farCornerRadiusSpring = flungCommittedFarCornerSpring,
                         alphaSpring = createSpring(1400f, 1f),
                 ),
-                scale = 0.85f,
-                scaleSpring = createSpring(6000f, 1f),
+                scale = 0.86f,
+                scaleSpring = createSpring(5700f, 1f),
         )
 
         flungIndicator = committedIndicator.copy(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 769edf7..1b49717 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -56,7 +56,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.SwipeHelper;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -748,7 +747,6 @@
                 !mKeyguardBypassController.getBypassEnabled());
 
         mSwipeHelper = mNotificationSwipeHelperBuilder
-                .setSwipeDirection(SwipeHelper.X)
                 .setNotificationCallback(mNotificationCallback)
                 .setOnMenuEventListener(mMenuEventListener)
                 .build();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index b476b68..91f53b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -76,11 +76,10 @@
             ViewConfiguration viewConfiguration,
             FalsingManager falsingManager,
             FeatureFlags featureFlags,
-            int swipeDirection,
             NotificationCallback callback,
             NotificationMenuRowPlugin.OnMenuEventListener menuListener,
             NotificationRoundnessManager notificationRoundnessManager) {
-        super(swipeDirection, callback, resources, viewConfiguration, falsingManager, featureFlags);
+        super(callback, resources, viewConfiguration, falsingManager, featureFlags);
         mNotificationRoundnessManager = notificationRoundnessManager;
         mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
         mMenuListener = menuListener;
@@ -416,22 +415,12 @@
     }
 
     @Override
-    public boolean swipedFastEnough(float translation, float viewSize) {
-        return swipedFastEnough();
-    }
-
-    @Override
     @VisibleForTesting
     protected boolean swipedFastEnough() {
         return super.swipedFastEnough();
     }
 
     @Override
-    public boolean swipedFarEnough(float translation, float viewSize) {
-        return swipedFarEnough();
-    }
-
-    @Override
     @VisibleForTesting
     protected boolean swipedFarEnough() {
         return super.swipedFarEnough();
@@ -554,7 +543,6 @@
         private final ViewConfiguration mViewConfiguration;
         private final FalsingManager mFalsingManager;
         private final FeatureFlags mFeatureFlags;
-        private int mSwipeDirection;
         private NotificationCallback mNotificationCallback;
         private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener;
         private NotificationRoundnessManager mNotificationRoundnessManager;
@@ -570,11 +558,6 @@
             mNotificationRoundnessManager = notificationRoundnessManager;
         }
 
-        Builder setSwipeDirection(int swipeDirection) {
-            mSwipeDirection = swipeDirection;
-            return this;
-        }
-
         Builder setNotificationCallback(NotificationCallback notificationCallback) {
             mNotificationCallback = notificationCallback;
             return this;
@@ -588,7 +571,7 @@
 
         NotificationSwipeHelper build() {
             return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager,
-                    mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener,
+                    mFeatureFlags, mNotificationCallback, mOnMenuEventListener,
                     mNotificationRoundnessManager);
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
new file mode 100644
index 0000000..891a6f8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.systemui.media.dialog;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.KeyguardManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.media.AudioManager;
+import android.media.session.MediaSessionManager;
+import android.os.PowerExemptionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.BluetoothMediaDevice;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
+
+    private static final String TEST_PACKAGE = "test_package";
+
+    // Mock
+    private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+    private final LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+    private final LocalBluetoothProfileManager mLocalBluetoothProfileManager = mock(
+            LocalBluetoothProfileManager.class);
+    private final LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast = mock(
+            LocalBluetoothLeBroadcast.class);
+    private final LocalBluetoothLeBroadcastAssistant mLocalBluetoothLeBroadcastAssistant = mock(
+            LocalBluetoothLeBroadcastAssistant.class);
+    private final BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata = mock(
+            BluetoothLeBroadcastMetadata.class);
+    private final BluetoothLeBroadcastReceiveState mBluetoothLeBroadcastReceiveState = mock(
+            BluetoothLeBroadcastReceiveState.class);
+    private final ActivityStarter mStarter = mock(ActivityStarter.class);
+    private final BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
+    private final LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
+    private final MediaDevice mBluetoothMediaDevice = mock(BluetoothMediaDevice.class);
+    private final BluetoothDevice mBluetoothDevice = mock(BluetoothDevice.class);
+    private final CachedBluetoothDevice mCachedBluetoothDevice = mock(CachedBluetoothDevice.class);
+    private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
+    private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
+            NearbyMediaDevicesManager.class);
+    private final AudioManager mAudioManager = mock(AudioManager.class);
+    private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
+    private KeyguardManager mKeyguardManager = mock(KeyguardManager.class);
+    private FeatureFlags mFlags = mock(FeatureFlags.class);
+
+    private MediaOutputBroadcastDialog mMediaOutputBroadcastDialog;
+    private MediaOutputController mMediaOutputController;
+
+    @Before
+    public void setUp() {
+        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(null);
+
+        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
+                mMediaSessionManager, mLocalBluetoothManager, mStarter,
+                mNotifCollection, mDialogLaunchAnimator,
+                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mKeyguardManager, mFlags);
+        mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
+        mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
+                mBroadcastSender, mMediaOutputController);
+        mMediaOutputBroadcastDialog.show();
+    }
+
+    @After
+    public void tearDown() {
+        mMediaOutputBroadcastDialog.dismissDialog();
+    }
+
+    @Test
+    public void connectBroadcastWithActiveDevice_noBroadcastMetadata_failToAddSource() {
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(null);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(
+                mLocalBluetoothLeBroadcastAssistant);
+
+        mMediaOutputBroadcastDialog.connectBroadcastWithActiveDevice();
+
+        verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean());
+    }
+
+    @Test
+    public void connectBroadcastWithActiveDevice_noConnectedMediaDevice_failToAddSource() {
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(
+                mBluetoothLeBroadcastMetadata);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(
+                mLocalBluetoothLeBroadcastAssistant);
+        when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(null);
+
+        mMediaOutputBroadcastDialog.connectBroadcastWithActiveDevice();
+
+        verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean());
+    }
+
+    @Test
+    public void connectBroadcastWithActiveDevice_hasBroadcastSource_failToAddSource() {
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(
+                mBluetoothLeBroadcastMetadata);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(
+                mLocalBluetoothLeBroadcastAssistant);
+        when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mBluetoothMediaDevice);
+        when(((BluetoothMediaDevice) mBluetoothMediaDevice).getCachedDevice())
+                .thenReturn(mCachedBluetoothDevice);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+        sourceList.add(mBluetoothLeBroadcastReceiveState);
+        when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothDevice)).thenReturn(
+                sourceList);
+
+        mMediaOutputBroadcastDialog.connectBroadcastWithActiveDevice();
+
+        verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean());
+    }
+
+    @Test
+    public void connectBroadcastWithActiveDevice_noBroadcastSource_failToAddSource() {
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(
+                mLocalBluetoothLeBroadcastAssistant);
+        when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(
+                mBluetoothLeBroadcastMetadata);
+        when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mBluetoothMediaDevice);
+        when(mBluetoothMediaDevice.isBLEDevice()).thenReturn(true);
+        when(((BluetoothMediaDevice) mBluetoothMediaDevice).getCachedDevice()).thenReturn(
+                mCachedBluetoothDevice);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+        when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothDevice)).thenReturn(
+                sourceList);
+
+        mMediaOutputBroadcastDialog.connectBroadcastWithActiveDevice();
+
+        verify(mLocalBluetoothLeBroadcastAssistant, times(1)).addSource(any(), any(), anyBoolean());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 824eb4a..551499e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -43,7 +43,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SwipeHelper;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.flags.FeatureFlags;
@@ -101,7 +100,6 @@
                 ViewConfiguration.get(mContext),
                 new FalsingManagerFake(),
                 mFeatureFlags,
-                SwipeHelper.X,
                 mCallback,
                 mListener,
                 mNotificationRoundnessManager));
diff --git a/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java b/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
index fa30a6f..e605514 100644
--- a/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
+++ b/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
@@ -123,11 +123,9 @@
     private static final int SCREEN_DEFAULT_COLOR_WITH_ALPHA =
             SCREEN_DEFAULT_COLOR | SCREEN_DEFAULT_ALPHA;
 
-    // TODO(b/266775677): Make protected-broadcast intent
     @VisibleForTesting
     static final String ACTION_FLASH_NOTIFICATION_START_PREVIEW =
             "com.android.internal.intent.action.FLASH_NOTIFICATION_START_PREVIEW";
-    // TODO(b/266775677): Make protected-broadcast intent
     @VisibleForTesting
     static final String ACTION_FLASH_NOTIFICATION_STOP_PREVIEW =
             "com.android.internal.intent.action.FLASH_NOTIFICATION_STOP_PREVIEW";
@@ -143,13 +141,10 @@
     @VisibleForTesting
     static final int PREVIEW_TYPE_LONG = 1;
 
-    // TODO(b/266775683): Move to settings provider
     @VisibleForTesting
     static final String SETTING_KEY_CAMERA_FLASH_NOTIFICATION = "camera_flash_notification";
-    // TODO(b/266775683): Move to settings provider
     @VisibleForTesting
     static final String SETTING_KEY_SCREEN_FLASH_NOTIFICATION = "screen_flash_notification";
-    // TODO(b/266775683): Move to settings provider
     @VisibleForTesting
     static final String SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR =
             "screen_flash_notification_color_global";
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 7f24c52..3d3535d 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -25,7 +25,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.Preconditions;
-import com.android.server.am.ActivityManagerService;
+import com.android.server.am.StackTracesDumpHelper;
 import com.android.server.utils.TimingsTraceAndSlog;
 
 import java.io.PrintWriter;
@@ -188,12 +188,12 @@
     }
 
     /**
-     * A helper function to call ActivityManagerService.dumpStackTraces().
+     * A helper function to call StackTracesDumpHelper.dumpStackTraces().
      */
     private static void dumpStackTraces() {
         final ArrayList<Integer> pids = new ArrayList<>();
         pids.add(Process.myPid());
-        ActivityManagerService.dumpStackTraces(pids,
+        StackTracesDumpHelper.dumpStackTraces(pids,
                 /* processCpuTracker= */null, /* lastPids= */null,
                 CompletableFuture.completedFuture(Watchdog.getInterestingNativePids()),
                 /* logExceptionCreatingFile= */null, /* subject= */null,
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 03821db..62651dd 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -56,6 +56,7 @@
 import com.android.internal.os.ZygoteConnectionConstants;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.am.StackTracesDumpHelper;
 import com.android.server.am.TraceErrorLogger;
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.wm.SurfaceAnimationThread;
@@ -905,7 +906,7 @@
         report.append(ResourcePressureUtil.currentPsiState());
         ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false);
         StringWriter tracesFileException = new StringWriter();
-        final File stack = ActivityManagerService.dumpStackTraces(
+        final File stack = StackTracesDumpHelper.dumpStackTraces(
                 pids, processCpuTracker, new SparseBooleanArray(),
                 CompletableFuture.completedFuture(getInterestingNativePids()), tracesFileException,
                 subject, criticalEvents, Runnable::run, /* latencyTracker= */null);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f2b6306..713b993 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -114,7 +114,6 @@
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
-import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -123,7 +122,6 @@
 import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
@@ -372,7 +370,6 @@
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -409,7 +406,6 @@
 import com.android.internal.os.TimeoutRecord;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
-import com.android.internal.os.anr.AnrLatencyTracker;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
@@ -488,14 +484,10 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -506,18 +498,13 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 
 public class ActivityManagerService extends IActivityManager.Stub
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
@@ -555,8 +542,6 @@
 
     static final String SYSTEM_USER_HOME_NEEDED = "ro.system_user_home_needed";
 
-    public static final String ANR_TRACE_DIR = "/data/anr";
-
     // Maximum number of receivers an app can register.
     private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
 
@@ -618,10 +603,6 @@
     private static final int MAX_BUGREPORT_TITLE_SIZE = 100;
     private static final int MAX_BUGREPORT_DESCRIPTION_SIZE = 150;
 
-    private static final int NATIVE_DUMP_TIMEOUT_MS =
-            2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds;
-    private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
-
     OomAdjuster mOomAdjuster;
 
     static final String EXTRA_TITLE = "android.intent.extra.TITLE";
@@ -3428,432 +3409,6 @@
         }
     }
 
-    /**
-     * If a stack trace dump file is configured, dump process stack traces.
-     * @param firstPids of dalvik VM processes to dump stack traces for first
-     * @param lastPids of dalvik VM processes to dump stack traces for last
-     * @param nativePids optional list of native pids to dump stack crawls
-     * @param logExceptionCreatingFile optional writer to which we log errors creating the file
-     * @param auxiliaryTaskExecutor executor to execute auxiliary tasks on
-     * @param latencyTracker the latency tracker instance of the current ANR.
-     */
-    public static File dumpStackTraces(ArrayList<Integer> firstPids,
-            ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
-            Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
-            @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
-        return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
-                logExceptionCreatingFile, null, null, null, auxiliaryTaskExecutor, latencyTracker);
-    }
-
-    /**
-     * If a stack trace dump file is configured, dump process stack traces.
-     * @param firstPids of dalvik VM processes to dump stack traces for first
-     * @param lastPids of dalvik VM processes to dump stack traces for last
-     * @param nativePids optional list of native pids to dump stack crawls
-     * @param logExceptionCreatingFile optional writer to which we log errors creating the file
-     * @param subject optional line related to the error
-     * @param criticalEventSection optional lines containing recent critical events.
-     * @param auxiliaryTaskExecutor executor to execute auxiliary tasks on
-     * @param latencyTracker the latency tracker instance of the current ANR.
-     */
-    public static File dumpStackTraces(ArrayList<Integer> firstPids,
-            ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
-            Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
-            String subject, String criticalEventSection, @NonNull Executor auxiliaryTaskExecutor,
-            AnrLatencyTracker latencyTracker) {
-        return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
-                logExceptionCreatingFile, null, subject, criticalEventSection,
-                auxiliaryTaskExecutor, latencyTracker);
-    }
-
-    /**
-     * @param firstPidEndOffset Optional, when it's set, it receives the start/end offset
-     *                        of the very first pid to be dumped.
-     */
-    /* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
-            ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
-            Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
-            AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
-            @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
-        try {
-
-            if (latencyTracker != null) {
-                latencyTracker.dumpStackTracesStarted();
-            }
-
-            Slog.i(TAG, "dumpStackTraces pids=" + lastPids);
-
-            // Measure CPU usage as soon as we're called in order to get a realistic sampling
-            // of the top users at the time of the request.
-            Supplier<ArrayList<Integer>> extraPidsSupplier = processCpuTracker != null
-                    ? () -> getExtraPids(processCpuTracker, lastPids, latencyTracker) : null;
-            Future<ArrayList<Integer>> extraPidsFuture = null;
-            if (extraPidsSupplier != null) {
-                extraPidsFuture =
-                        CompletableFuture.supplyAsync(extraPidsSupplier, auxiliaryTaskExecutor);
-            }
-
-            final File tracesDir = new File(ANR_TRACE_DIR);
-
-            // NOTE: We should consider creating the file in native code atomically once we've
-            // gotten rid of the old scheme of dumping and lot of the code that deals with paths
-            // can be removed.
-            File tracesFile;
-            try {
-                tracesFile = createAnrDumpFile(tracesDir);
-            } catch (IOException e) {
-                Slog.w(TAG, "Exception creating ANR dump file:", e);
-                if (logExceptionCreatingFile != null) {
-                    logExceptionCreatingFile.append(
-                            "----- Exception creating ANR dump file -----\n");
-                    e.printStackTrace(new PrintWriter(logExceptionCreatingFile));
-                }
-                if (latencyTracker != null) {
-                    latencyTracker.anrSkippedDumpStackTraces();
-                }
-                return null;
-            }
-
-            if (subject != null || criticalEventSection != null) {
-                appendtoANRFile(tracesFile.getAbsolutePath(),
-                        (subject != null ? "Subject: " + subject + "\n\n" : "")
-                        + (criticalEventSection != null ? criticalEventSection : ""));
-            }
-
-            long firstPidEndPos = dumpStackTraces(
-                    tracesFile.getAbsolutePath(), firstPids, nativePidsFuture,
-                    extraPidsFuture, latencyTracker);
-            if (firstPidEndOffset != null) {
-                firstPidEndOffset.set(firstPidEndPos);
-            }
-            // Each set of ANR traces is written to a separate file and dumpstate will process
-            // all such files and add them to a captured bug report if they're recent enough.
-            maybePruneOldTraces(tracesDir);
-
-            return tracesFile;
-        } finally {
-            if (latencyTracker != null) {
-                latencyTracker.dumpStackTracesEnded();
-            }
-        }
-
-    }
-
-    @GuardedBy("ActivityManagerService.class")
-    private static SimpleDateFormat sAnrFileDateFormat;
-    static final String ANR_FILE_PREFIX = "anr_";
-
-    private static ArrayList<Integer> getExtraPids(ProcessCpuTracker processCpuTracker,
-            SparseBooleanArray lastPids, AnrLatencyTracker latencyTracker) {
-        if (latencyTracker != null) {
-            latencyTracker.processCpuTrackerMethodsCalled();
-        }
-        ArrayList<Integer> extraPids = new ArrayList<>();
-        processCpuTracker.init();
-        try {
-            Thread.sleep(200);
-        } catch (InterruptedException ignored) {
-        }
-
-        processCpuTracker.update();
-
-        // We'll take the stack crawls of just the top apps using CPU.
-        final int workingStatsNumber = processCpuTracker.countWorkingStats();
-        for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
-            ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
-            if (lastPids.indexOfKey(stats.pid) >= 0) {
-                if (DEBUG_ANR) {
-                    Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
-                }
-
-                extraPids.add(stats.pid);
-            } else {
-                Slog.i(TAG,
-                        "Skipping next CPU consuming process, not a java proc: "
-                        + stats.pid);
-            }
-        }
-        if (latencyTracker != null) {
-            latencyTracker.processCpuTrackerMethodsReturned();
-        }
-        return extraPids;
-    }
-
-    private static synchronized File createAnrDumpFile(File tracesDir) throws IOException {
-        if (sAnrFileDateFormat == null) {
-            sAnrFileDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
-        }
-
-        final String formattedDate = sAnrFileDateFormat.format(new Date());
-        final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate);
-
-        if (anrFile.createNewFile()) {
-            FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw-------
-            return anrFile;
-        } else {
-            throw new IOException("Unable to create ANR dump file: createNewFile failed");
-        }
-    }
-
-    /**
-     * Prune all trace files that are more than a day old.
-     *
-     * NOTE: It might make sense to move this functionality to tombstoned eventually, along with a
-     * shift away from anr_XX and tombstone_XX to a more descriptive name. We do it here for now
-     * since it's the system_server that creates trace files for most ANRs.
-     */
-    private static void maybePruneOldTraces(File tracesDir) {
-        final File[] files = tracesDir.listFiles();
-        if (files == null) return;
-
-        final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
-        final long now = System.currentTimeMillis();
-        try {
-            Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
-            for (int i = 0; i < files.length; ++i) {
-                if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
-                    if (!files[i].delete()) {
-                        Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
-                    }
-                }
-            }
-        } catch (IllegalArgumentException e) {
-            // The modification times changed while we were sorting. Bail...
-            // https://issuetracker.google.com/169836837
-            Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
-        }
-    }
-
-    /**
-     * Dump java traces for process {@code pid} to the specified file. If java trace dumping
-     * fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
-     * to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent
-     * attempting to obtain native traces in the case of a failure. Returns the total time spent
-     * capturing traces.
-     */
-    private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs) {
-        final long timeStart = SystemClock.elapsedRealtime();
-        int headerSize = writeUptimeStartHeaderForPid(pid, fileName);
-        boolean javaSuccess = Debug.dumpJavaBacktraceToFileTimeout(pid, fileName,
-                (int) (timeoutMs / 1000));
-        if (javaSuccess) {
-            // Check that something is in the file, actually. Try-catch should not be necessary,
-            // but better safe than sorry.
-            try {
-                long size = new File(fileName).length();
-                if ((size - headerSize) < JAVA_DUMP_MINIMUM_SIZE) {
-                    Slog.w(TAG, "Successfully created Java ANR file is empty!");
-                    javaSuccess = false;
-                }
-            } catch (Exception e) {
-                Slog.w(TAG, "Unable to get ANR file size", e);
-                javaSuccess = false;
-            }
-        }
-        if (!javaSuccess) {
-            Slog.w(TAG, "Dumping Java threads failed, initiating native stack dump.");
-            if (!Debug.dumpNativeBacktraceToFileTimeout(pid, fileName,
-                    (NATIVE_DUMP_TIMEOUT_MS / 1000))) {
-                Slog.w(TAG, "Native stack dump failed!");
-            }
-        }
-
-        return SystemClock.elapsedRealtime() - timeStart;
-    }
-
-    private static int appendtoANRFile(String fileName, String text) {
-        try (FileOutputStream fos = new FileOutputStream(fileName, true)) {
-            byte[] header = text.getBytes(StandardCharsets.UTF_8);
-            fos.write(header);
-            return header.length;
-        } catch (IOException e) {
-            Slog.w(TAG, "Exception writing to ANR dump file:", e);
-            return 0;
-        }
-    }
-
-    /*
-     * Writes a header containing the process id and the current system uptime.
-     */
-    private static int writeUptimeStartHeaderForPid(int pid, String fileName) {
-        return appendtoANRFile(fileName, "----- dumping pid: " + pid + " at "
-            + SystemClock.uptimeMillis() + "\n");
-    }
-
-
-    /**
-     * @return The end offset of the trace of the very first PID
-     */
-    public static long dumpStackTraces(String tracesFile,
-            ArrayList<Integer> firstPids, Future<ArrayList<Integer>> nativePidsFuture,
-            Future<ArrayList<Integer>> extraPidsFuture, AnrLatencyTracker latencyTracker) {
-
-        Slog.i(TAG, "Dumping to " + tracesFile);
-
-        // We don't need any sort of inotify based monitoring when we're dumping traces via
-        // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
-        // control of all writes to the file in question.
-
-        // We must complete all stack dumps within 20 seconds.
-        long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
-
-        // As applications are usually interested with the ANR stack traces, but we can't share with
-        // them the stack traces other than their own stacks. So after the very first PID is
-        // dumped, remember the current file size.
-        long firstPidEnd = -1;
-
-        // First collect all of the stacks of the most important pids.
-        if (firstPids != null) {
-            if (latencyTracker != null) {
-                latencyTracker.dumpingFirstPidsStarted();
-            }
-
-            int num = firstPids.size();
-            for (int i = 0; i < num; i++) {
-                final int pid = firstPids.get(i);
-                // We don't copy ANR traces from the system_server intentionally.
-                final boolean firstPid = i == 0 && MY_PID != pid;
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidStarted(pid);
-                }
-
-                Slog.i(TAG, "Collecting stacks for pid " + pid);
-                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
-                                                                remainingTime);
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidEnded();
-                }
-
-                remainingTime -= timeTaken;
-                if (remainingTime <= 0) {
-                    Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
-                            + "); deadline exceeded.");
-                    return firstPidEnd;
-                }
-
-                if (firstPid) {
-                    firstPidEnd = new File(tracesFile).length();
-                    // Full latency dump
-                    if (latencyTracker != null) {
-                        appendtoANRFile(tracesFile,
-                                latencyTracker.dumpAsCommaSeparatedArrayWithHeader());
-                    }
-                }
-                if (DEBUG_ANR) {
-                    Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
-                }
-            }
-            if (latencyTracker != null) {
-                latencyTracker.dumpingFirstPidsEnded();
-            }
-        }
-
-        // Next collect the stacks of the native pids
-        ArrayList<Integer> nativePids = collectPids(nativePidsFuture, "native pids");
-
-        Slog.i(TAG, "dumpStackTraces nativepids=" + nativePids);
-
-        if (nativePids != null) {
-            if (latencyTracker != null) {
-                latencyTracker.dumpingNativePidsStarted();
-            }
-            for (int pid : nativePids) {
-                Slog.i(TAG, "Collecting stacks for native pid " + pid);
-                final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
-
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidStarted(pid);
-                }
-                final long start = SystemClock.elapsedRealtime();
-                Debug.dumpNativeBacktraceToFileTimeout(
-                        pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
-                final long timeTaken = SystemClock.elapsedRealtime() - start;
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidEnded();
-                }
-                remainingTime -= timeTaken;
-                if (remainingTime <= 0) {
-                    Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
-                        "); deadline exceeded.");
-                    return firstPidEnd;
-                }
-
-                if (DEBUG_ANR) {
-                    Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
-                }
-            }
-            if (latencyTracker != null) {
-                latencyTracker.dumpingNativePidsEnded();
-            }
-        }
-
-        // Lastly, dump stacks for all extra PIDs from the CPU tracker.
-        ArrayList<Integer> extraPids = collectPids(extraPidsFuture, "extra pids");
-
-        if (extraPidsFuture != null) {
-            try {
-                extraPids = extraPidsFuture.get();
-            } catch (ExecutionException e) {
-                Slog.w(TAG, "Failed to collect extra pids", e.getCause());
-            } catch (InterruptedException e) {
-                Slog.w(TAG, "Interrupted while collecting extra pids", e);
-            }
-        }
-        Slog.i(TAG, "dumpStackTraces extraPids=" + extraPids);
-
-        if (extraPids != null) {
-            if (latencyTracker != null) {
-                latencyTracker.dumpingExtraPidsStarted();
-            }
-            for (int pid : extraPids) {
-                Slog.i(TAG, "Collecting stacks for extra pid " + pid);
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidStarted(pid);
-                }
-                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
-                if (latencyTracker != null) {
-                    latencyTracker.dumpingPidEnded();
-                }
-                remainingTime -= timeTaken;
-                if (remainingTime <= 0) {
-                    Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
-                            "); deadline exceeded.");
-                    return firstPidEnd;
-                }
-
-                if (DEBUG_ANR) {
-                    Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
-                }
-            }
-            if (latencyTracker != null) {
-                latencyTracker.dumpingExtraPidsEnded();
-            }
-        }
-        // Append the dumping footer with the current uptime
-        appendtoANRFile(tracesFile, "----- dumping ended at " + SystemClock.uptimeMillis() + "\n");
-        Slog.i(TAG, "Done dumping");
-
-        return firstPidEnd;
-    }
-
-    private static ArrayList<Integer> collectPids(Future<ArrayList<Integer>> pidsFuture,
-            String logName) {
-
-        ArrayList<Integer> pids = null;
-
-        if (pidsFuture == null) {
-            return pids;
-        }
-        try {
-            pids = pidsFuture.get();
-        } catch (ExecutionException e) {
-            Slog.w(TAG, "Failed to collect " + logName, e.getCause());
-        } catch (InterruptedException e) {
-            Slog.w(TAG, "Interrupted while collecting " + logName , e);
-        }
-        return pids;
-    }
-
     @Override
     public boolean clearApplicationUserData(final String packageName, boolean keepState,
             final IPackageDataObserver observer, int userId) {
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 1ba3266..4443636 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -1153,7 +1153,7 @@
         final ArraySet<String> allFiles = new ArraySet();
         final File[] files = mProcExitStoreDir.listFiles((f) -> {
             final String name = f.getName();
-            boolean trace = name.startsWith(ActivityManagerService.ANR_FILE_PREFIX)
+            boolean trace = name.startsWith(StackTracesDumpHelper.ANR_FILE_PREFIX)
                     && name.endsWith(APP_TRACE_FILE_SUFFIX);
             if (trace) {
                 allFiles.add(name);
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 2517c9c1..1d48cb2 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -493,7 +493,7 @@
         StringWriter tracesFileException = new StringWriter();
         // To hold the start and end offset to the ANR trace file respectively.
         final AtomicLong firstPidEndOffset = new AtomicLong(-1);
-        File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+        File tracesFile = StackTracesDumpHelper.dumpStackTraces(firstPids,
                 isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
                 nativePidsFuture, tracesFileException, firstPidEndOffset, annotation,
                 criticalEventLog, auxiliaryTaskExecutor, latencyTracker);
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
new file mode 100644
index 0000000..9373328
--- /dev/null
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -0,0 +1,483 @@
+/*
+ * 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.server.am;
+
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.Debug;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.anr.AnrLatencyTracker;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+
+
+/**
+ * A helper for dumping stack traces.
+ */
+public class StackTracesDumpHelper {
+    static final String TAG = TAG_WITH_CLASS_NAME ? "StackTracesDumpHelper" : TAG_AM;
+
+    @GuardedBy("StackTracesDumpHelper.class")
+    private static final SimpleDateFormat ANR_FILE_DATE_FORMAT =
+            new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
+
+    static final String ANR_FILE_PREFIX = "anr_";
+    public static final String ANR_TRACE_DIR = "/data/anr";
+
+    private static final int NATIVE_DUMP_TIMEOUT_MS =
+            2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds;
+    private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
+
+    /**
+     * If a stack trace dump file is configured, dump process stack traces.
+     * @param firstPids of dalvik VM processes to dump stack traces for first
+     * @param lastPids of dalvik VM processes to dump stack traces for last
+     * @param nativePidsFuture optional future for a list of native pids to dump stack crawls
+     * @param logExceptionCreatingFile optional writer to which we log errors creating the file
+     * @param auxiliaryTaskExecutor executor to execute auxiliary tasks on
+     * @param latencyTracker the latency tracker instance of the current ANR.
+     */
+    public static File dumpStackTraces(ArrayList<Integer> firstPids,
+            ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
+            Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
+            @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
+        return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
+                logExceptionCreatingFile, null, null, null, auxiliaryTaskExecutor, latencyTracker);
+    }
+
+    /**
+     * @param subject the subject of the dumped traces
+     * @param criticalEventSection the critical event log, passed as a string
+     */
+    public static File dumpStackTraces(ArrayList<Integer> firstPids,
+            ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
+            Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
+            String subject, String criticalEventSection, @NonNull Executor auxiliaryTaskExecutor,
+            AnrLatencyTracker latencyTracker) {
+        return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
+                logExceptionCreatingFile, null, subject, criticalEventSection,
+                auxiliaryTaskExecutor, latencyTracker);
+    }
+
+    /**
+     * @param firstPidEndOffset Optional, when it's set, it receives the start/end offset
+     *                        of the very first pid to be dumped.
+     */
+    /* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
+            ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
+            Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
+            AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
+            @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
+        try {
+
+            if (latencyTracker != null) {
+                latencyTracker.dumpStackTracesStarted();
+            }
+
+            Slog.i(TAG, "dumpStackTraces pids=" + lastPids);
+
+            // Measure CPU usage as soon as we're called in order to get a realistic sampling
+            // of the top users at the time of the request.
+            Supplier<ArrayList<Integer>> extraPidsSupplier = processCpuTracker != null
+                    ? () -> getExtraPids(processCpuTracker, lastPids, latencyTracker) : null;
+            Future<ArrayList<Integer>> extraPidsFuture = null;
+            if (extraPidsSupplier != null) {
+                extraPidsFuture =
+                        CompletableFuture.supplyAsync(extraPidsSupplier, auxiliaryTaskExecutor);
+            }
+
+            final File tracesDir = new File(ANR_TRACE_DIR);
+
+            // NOTE: We should consider creating the file in native code atomically once we've
+            // gotten rid of the old scheme of dumping and lot of the code that deals with paths
+            // can be removed.
+            File tracesFile;
+            try {
+                tracesFile = createAnrDumpFile(tracesDir);
+            } catch (IOException e) {
+                Slog.w(TAG, "Exception creating ANR dump file:", e);
+                if (logExceptionCreatingFile != null) {
+                    logExceptionCreatingFile.append(
+                            "----- Exception creating ANR dump file -----\n");
+                    e.printStackTrace(new PrintWriter(logExceptionCreatingFile));
+                }
+                if (latencyTracker != null) {
+                    latencyTracker.anrSkippedDumpStackTraces();
+                }
+                return null;
+            }
+
+            if (subject != null || criticalEventSection != null) {
+                appendtoANRFile(tracesFile.getAbsolutePath(),
+                        (subject != null ? "Subject: " + subject + "\n\n" : "")
+                        + (criticalEventSection != null ? criticalEventSection : ""));
+            }
+
+            long firstPidEndPos = dumpStackTraces(
+                    tracesFile.getAbsolutePath(), firstPids, nativePidsFuture,
+                    extraPidsFuture, latencyTracker);
+            if (firstPidEndOffset != null) {
+                firstPidEndOffset.set(firstPidEndPos);
+            }
+            // Each set of ANR traces is written to a separate file and dumpstate will process
+            // all such files and add them to a captured bug report if they're recent enough.
+            maybePruneOldTraces(tracesDir);
+
+            return tracesFile;
+        } finally {
+            if (latencyTracker != null) {
+                latencyTracker.dumpStackTracesEnded();
+            }
+        }
+
+    }
+
+    /**
+     * @return The end offset of the trace of the very first PID
+     */
+    public static long dumpStackTraces(String tracesFile,
+            ArrayList<Integer> firstPids, Future<ArrayList<Integer>> nativePidsFuture,
+            Future<ArrayList<Integer>> extraPidsFuture, AnrLatencyTracker latencyTracker) {
+
+        Slog.i(TAG, "Dumping to " + tracesFile);
+
+        // We don't need any sort of inotify based monitoring when we're dumping traces via
+        // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
+        // control of all writes to the file in question.
+
+        // We must complete all stack dumps within 20 seconds.
+        long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
+
+        // As applications are usually interested with the ANR stack traces, but we can't share with
+        // them the stack traces other than their own stacks. So after the very first PID is
+        // dumped, remember the current file size.
+        long firstPidEnd = -1;
+
+        // First collect all of the stacks of the most important pids.
+        if (firstPids != null) {
+            if (latencyTracker != null) {
+                latencyTracker.dumpingFirstPidsStarted();
+            }
+
+            int num = firstPids.size();
+            for (int i = 0; i < num; i++) {
+                final int pid = firstPids.get(i);
+                // We don't copy ANR traces from the system_server intentionally.
+                final boolean firstPid = i == 0 && ActivityManagerService.MY_PID != pid;
+                if (latencyTracker != null) {
+                    latencyTracker.dumpingPidStarted(pid);
+                }
+
+                Slog.i(TAG, "Collecting stacks for pid " + pid);
+                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
+                                                                remainingTime);
+                if (latencyTracker != null) {
+                    latencyTracker.dumpingPidEnded();
+                }
+
+                remainingTime -= timeTaken;
+                if (remainingTime <= 0) {
+                    Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
+                            + "); deadline exceeded.");
+                    return firstPidEnd;
+                }
+
+                if (firstPid) {
+                    firstPidEnd = new File(tracesFile).length();
+                    // Full latency dump
+                    if (latencyTracker != null) {
+                        appendtoANRFile(tracesFile,
+                                latencyTracker.dumpAsCommaSeparatedArrayWithHeader());
+                    }
+                }
+                if (DEBUG_ANR) {
+                    Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
+                }
+            }
+            if (latencyTracker != null) {
+                latencyTracker.dumpingFirstPidsEnded();
+            }
+        }
+
+        // Next collect the stacks of the native pids
+        ArrayList<Integer> nativePids = collectPids(nativePidsFuture, "native pids");
+
+        Slog.i(TAG, "dumpStackTraces nativepids=" + nativePids);
+
+        if (nativePids != null) {
+            if (latencyTracker != null) {
+                latencyTracker.dumpingNativePidsStarted();
+            }
+            for (int pid : nativePids) {
+                Slog.i(TAG, "Collecting stacks for native pid " + pid);
+                final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
+
+                if (latencyTracker != null) {
+                    latencyTracker.dumpingPidStarted(pid);
+                }
+                final long start = SystemClock.elapsedRealtime();
+                Debug.dumpNativeBacktraceToFileTimeout(
+                        pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
+                final long timeTaken = SystemClock.elapsedRealtime() - start;
+                if (latencyTracker != null) {
+                    latencyTracker.dumpingPidEnded();
+                }
+                remainingTime -= timeTaken;
+                if (remainingTime <= 0) {
+                    Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid
+                            + "); deadline exceeded.");
+                    return firstPidEnd;
+                }
+
+                if (DEBUG_ANR) {
+                    Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
+                }
+            }
+            if (latencyTracker != null) {
+                latencyTracker.dumpingNativePidsEnded();
+            }
+        }
+
+        // Lastly, dump stacks for all extra PIDs from the CPU tracker.
+        ArrayList<Integer> extraPids = collectPids(extraPidsFuture, "extra pids");
+
+        if (extraPidsFuture != null) {
+            try {
+                extraPids = extraPidsFuture.get();
+            } catch (ExecutionException e) {
+                Slog.w(TAG, "Failed to collect extra pids", e.getCause());
+            } catch (InterruptedException e) {
+                Slog.w(TAG, "Interrupted while collecting extra pids", e);
+            }
+        }
+        Slog.i(TAG, "dumpStackTraces extraPids=" + extraPids);
+
+        if (extraPids != null) {
+            if (latencyTracker != null) {
+                latencyTracker.dumpingExtraPidsStarted();
+            }
+            for (int pid : extraPids) {
+                Slog.i(TAG, "Collecting stacks for extra pid " + pid);
+                if (latencyTracker != null) {
+                    latencyTracker.dumpingPidStarted(pid);
+                }
+                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
+                if (latencyTracker != null) {
+                    latencyTracker.dumpingPidEnded();
+                }
+                remainingTime -= timeTaken;
+                if (remainingTime <= 0) {
+                    Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid
+                            + "); deadline exceeded.");
+                    return firstPidEnd;
+                }
+
+                if (DEBUG_ANR) {
+                    Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
+                }
+            }
+            if (latencyTracker != null) {
+                latencyTracker.dumpingExtraPidsEnded();
+            }
+        }
+        // Append the dumping footer with the current uptime
+        appendtoANRFile(tracesFile, "----- dumping ended at " + SystemClock.uptimeMillis() + "\n");
+        Slog.i(TAG, "Done dumping");
+
+        return firstPidEnd;
+    }
+
+    private static synchronized File createAnrDumpFile(File tracesDir) throws IOException {
+        final String formattedDate = ANR_FILE_DATE_FORMAT.format(new Date());
+        final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate);
+
+        if (anrFile.createNewFile()) {
+            FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw-------
+            return anrFile;
+        } else {
+            throw new IOException("Unable to create ANR dump file: createNewFile failed");
+        }
+    }
+
+    private static ArrayList<Integer> getExtraPids(ProcessCpuTracker processCpuTracker,
+            SparseBooleanArray lastPids, AnrLatencyTracker latencyTracker) {
+        if (latencyTracker != null) {
+            latencyTracker.processCpuTrackerMethodsCalled();
+        }
+        ArrayList<Integer> extraPids = new ArrayList<>();
+        processCpuTracker.init();
+        try {
+            Thread.sleep(200);
+        } catch (InterruptedException ignored) {
+        }
+
+        processCpuTracker.update();
+
+        // We'll take the stack crawls of just the top apps using CPU.
+        final int workingStatsNumber = processCpuTracker.countWorkingStats();
+        for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
+            ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
+            if (lastPids.indexOfKey(stats.pid) >= 0) {
+                if (DEBUG_ANR) {
+                    Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+                }
+
+                extraPids.add(stats.pid);
+            } else {
+                Slog.i(TAG,
+                        "Skipping next CPU consuming process, not a java proc: "
+                        + stats.pid);
+            }
+        }
+        if (latencyTracker != null) {
+            latencyTracker.processCpuTrackerMethodsReturned();
+        }
+        return extraPids;
+    }
+
+    /**
+     * Prune all trace files that are more than a day old.
+     *
+     * NOTE: It might make sense to move this functionality to tombstoned eventually, along with a
+     * shift away from anr_XX and tombstone_XX to a more descriptive name. We do it here for now
+     * since it's the system_server that creates trace files for most ANRs.
+     */
+    private static void maybePruneOldTraces(File tracesDir) {
+        final File[] files = tracesDir.listFiles();
+        if (files == null) return;
+
+        final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
+        final long now = System.currentTimeMillis();
+        try {
+            Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+            for (int i = 0; i < files.length; ++i) {
+                if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+                    if (!files[i].delete()) {
+                        Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+                    }
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            // The modification times changed while we were sorting. Bail...
+            // https://issuetracker.google.com/169836837
+            Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
+        }
+    }
+    /**
+     * Dump java traces for process {@code pid} to the specified file. If java trace dumping
+     * fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
+     * to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent
+     * attempting to obtain native traces in the case of a failure. Returns the total time spent
+     * capturing traces.
+     */
+    private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs) {
+        final long timeStart = SystemClock.elapsedRealtime();
+        int headerSize = writeUptimeStartHeaderForPid(pid, fileName);
+        boolean javaSuccess = Debug.dumpJavaBacktraceToFileTimeout(pid, fileName,
+                (int) (timeoutMs / 1000));
+        if (javaSuccess) {
+            // Check that something is in the file, actually. Try-catch should not be necessary,
+            // but better safe than sorry.
+            try {
+                long size = new File(fileName).length();
+                if ((size - headerSize) < JAVA_DUMP_MINIMUM_SIZE) {
+                    Slog.w(TAG, "Successfully created Java ANR file is empty!");
+                    javaSuccess = false;
+                }
+            } catch (Exception e) {
+                Slog.w(TAG, "Unable to get ANR file size", e);
+                javaSuccess = false;
+            }
+        }
+        if (!javaSuccess) {
+            Slog.w(TAG, "Dumping Java threads failed, initiating native stack dump.");
+            if (!Debug.dumpNativeBacktraceToFileTimeout(pid, fileName,
+                    (NATIVE_DUMP_TIMEOUT_MS / 1000))) {
+                Slog.w(TAG, "Native stack dump failed!");
+            }
+        }
+
+        return SystemClock.elapsedRealtime() - timeStart;
+    }
+
+    private static int appendtoANRFile(String fileName, String text) {
+        try (FileOutputStream fos = new FileOutputStream(fileName, true)) {
+            byte[] header = text.getBytes(StandardCharsets.UTF_8);
+            fos.write(header);
+            return header.length;
+        } catch (IOException e) {
+            Slog.w(TAG, "Exception writing to ANR dump file:", e);
+            return 0;
+        }
+    }
+
+    /*
+     * Writes a header containing the process id and the current system uptime.
+     */
+    private static int writeUptimeStartHeaderForPid(int pid, String fileName) {
+        return appendtoANRFile(fileName, "----- dumping pid: " + pid + " at "
+            + SystemClock.uptimeMillis() + "\n");
+    }
+
+    private static ArrayList<Integer> collectPids(Future<ArrayList<Integer>> pidsFuture,
+            String logName) {
+
+        ArrayList<Integer> pids = null;
+
+        if (pidsFuture == null) {
+            return pids;
+        }
+        try {
+            pids = pidsFuture.get();
+        } catch (ExecutionException e) {
+            Slog.w(TAG, "Failed to collect " + logName, e.getCause());
+        } catch (InterruptedException e) {
+            Slog.w(TAG, "Interrupted while collecting " + logName , e);
+        }
+        return pids;
+    }
+
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ad5ae5f..745e404 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -4600,7 +4600,8 @@
         public void onDesiredDisplayModeSpecsChanged() {
             synchronized (mSyncRoot) {
                 mChanged = false;
-                mLogicalDisplayMapper.forEachLocked(mSpecsChangedConsumer);
+                mLogicalDisplayMapper.forEachLocked(mSpecsChangedConsumer,
+                        /* includeDisabled= */ false);
                 if (mChanged) {
                     scheduleTraversalLocked(false);
                     mChanged = false;
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 250ba43..424eedc 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -302,9 +302,16 @@
     }
 
     public void forEachLocked(Consumer<LogicalDisplay> consumer) {
+        forEachLocked(consumer, /* includeDisabled= */ true);
+    }
+
+    public void forEachLocked(Consumer<LogicalDisplay> consumer, boolean includeDisabled) {
         final int count = mLogicalDisplays.size();
         for (int i = 0; i < count; i++) {
-            consumer.accept(mLogicalDisplays.valueAt(i));
+            LogicalDisplay display = mLogicalDisplays.valueAt(i);
+            if (display.isEnabledLocked() || includeDisabled) {
+                consumer.accept(display);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java b/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
index abb8439..9058c98 100644
--- a/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
+++ b/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
@@ -80,7 +80,7 @@
     protected void handleEarcStateChange(@Constants.EarcStatus int status) {
         int oldEarcStatus;
         synchronized (mLock) {
-            HdmiLogger.debug(TAG, "eARC state change [old:%b new %b]", mEarcStatus,
+            HdmiLogger.debug("eARC state change [old:%b new %b]", mEarcStatus,
                     status);
             oldEarcStatus = mEarcStatus;
             mEarcStatus = status;
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index ceb9706..61fe654 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -284,7 +284,7 @@
 
     void setWindowState(IBinder windowToken, @NonNull ImeTargetWindowState newState) {
         final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
-        if (state != null && newState.hasEdiorFocused()) {
+        if (state != null && newState.hasEditorFocused()) {
             // Inherit the last requested IME visible state when the target window is still
             // focused with an editor.
             newState.setRequestedImeVisible(state.mRequestedImeVisible);
@@ -340,7 +340,7 @@
         // state is ALWAYS_HIDDEN or STATE_HIDDEN with forward navigation).
         // Because the app might leverage these flags to hide soft-keyboard with showing their own
         // UI for input.
-        if (state.hasEdiorFocused() && shouldRestoreImeVisibility(state)) {
+        if (state.hasEditorFocused() && shouldRestoreImeVisibility(state)) {
             if (DEBUG) Slog.v(TAG, "Will show input to restore visibility");
             // Inherit the last requested IME visible state when the target window is still
             // focused with an editor.
@@ -352,7 +352,7 @@
 
         switch (softInputVisibility) {
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
-                if (state.hasImeFocusChanged() && (!state.hasEdiorFocused() || !doAutoShow)) {
+                if (state.hasImeFocusChanged() && (!state.hasEditorFocused() || !doAutoShow)) {
                     if (WindowManager.LayoutParams.mayUseInputMethod(state.getWindowFlags())) {
                         // There is no focus view, and this window will
                         // be behind any soft input window, so hide the
@@ -361,7 +361,7 @@
                         return new ImeVisibilityResult(STATE_HIDE_IME_NOT_ALWAYS,
                                 SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
                     }
-                } else if (state.hasEdiorFocused() && doAutoShow && isForwardNavigation) {
+                } else if (state.hasEditorFocused() && doAutoShow && isForwardNavigation) {
                     // There is a focus view, and we are navigating forward
                     // into the window, so show the input window for the user.
                     // We only do this automatically if the window can resize
@@ -437,7 +437,7 @@
                         SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
             }
         }
-        if (!state.hasEdiorFocused() && mInputShown && state.isStartInputByGainFocus()
+        if (!state.hasEditorFocused() && mInputShown && state.isStartInputByGainFocus()
                 && mService.mInputMethodDeviceConfigs.shouldHideImeWhenNoEditorFocus()) {
             // Hide the soft-keyboard when the system do nothing for softInputModeState
             // of the window being gained focus without an editor. This behavior benefits
@@ -620,7 +620,7 @@
             return mImeFocusChanged;
         }
 
-        boolean hasEdiorFocused() {
+        boolean hasEditorFocused() {
             return mHasFocusedEditor;
         }
 
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index b4b8cb2..ad77ef7 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -28,6 +28,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Intent;
@@ -620,12 +621,15 @@
         extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
         extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
         final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
+        final Bundle options = new BroadcastOptions()
+                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+                .toBundle();
         handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
                 extras, flags, null /* targetPkg */, null /* finishedReceiver */,
                 new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
                 (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
                         mPm.snapshotComputer(), callingUid, intentExtras),
-                null /* bOptions */));
+                options));
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a36e9f9..927a722 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1276,7 +1276,15 @@
         getDevicePolicyManagerInternal().broadcastIntentToManifestReceivers(
                 intent, parentHandle, /* requiresPermission= */ true);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
-        mContext.sendBroadcastAsUser(intent, parentHandle);
+        final Bundle options = new BroadcastOptions()
+                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+                .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+                // Both actions use single namespace because only the final state matters.
+                .setDeliveryGroupMatchingKey(
+                        Intent.ACTION_MANAGED_PROFILE_AVAILABLE /* namespace */,
+                        String.valueOf(profileHandle.getIdentifier()) /* key */)
+                .toBundle();
+        mContext.sendBroadcastAsUser(intent, parentHandle, /* receiverPermission= */ null, options);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 88d64df..35e88c1 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -33,6 +33,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
@@ -178,6 +179,7 @@
     private final SessionMonitor mSessionMonitor;
     private int mCurrentUserId;
     private boolean mTracingEnabled;
+    private int mLastSystemKey = -1;
 
     private final TileRequestTracker mTileRequestTracker;
 
@@ -905,6 +907,8 @@
             return;
         }
 
+        mLastSystemKey = key;
+
         if (mBar != null) {
             try {
                 mBar.handleSystemKey(key);
@@ -914,6 +918,14 @@
     }
 
     @Override
+    @TestApi
+    public int getLastSystemKey() {
+        enforceStatusBar();
+
+        return mLastSystemKey;
+    }
+
+    @Override
     public void showPinningEnterExitToast(boolean entering) throws RemoteException {
         if (mBar != null) {
             try {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 12be1d3..ea2765d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2947,8 +2947,6 @@
 
         if (differentTopTask && !mAvoidMoveToFront) {
             mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-            // TODO(b/264487981): Consider using BackgroundActivityStartController to determine
-            //  whether to bring the launching activity to the front.
             if (mSourceRecord == null || inTopNonFinishingTask(mSourceRecord)) {
                 // We really do want to push this one into the user's face, right now.
                 if (mLaunchTaskBehind && mSourceRecord != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 555cd38..7b9cc6f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -75,9 +75,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
 import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
-import static com.android.server.am.ActivityManagerService.dumpStackTraces;
 import static com.android.server.am.ActivityManagerServiceDumpActivitiesProto.ROOT_WINDOW_CONTAINER;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONTROLLER;
@@ -95,6 +93,8 @@
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
 import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
 import static com.android.server.am.EventLogTags.writeConfigurationChanged;
+import static com.android.server.am.StackTracesDumpHelper.ANR_TRACE_DIR;
+import static com.android.server.am.StackTracesDumpHelper.dumpStackTraces;
 import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID;
 import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID;
 import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index bbe7a33..90ec964 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -34,7 +34,7 @@
 import android.view.InputApplicationHandle;
 
 import com.android.internal.os.TimeoutRecord;
-import com.android.server.am.ActivityManagerService;
+import com.android.server.am.StackTracesDumpHelper;
 import com.android.server.criticalevents.CriticalEventLog;
 
 import java.io.File;
@@ -336,7 +336,7 @@
 
             String criticalEvents =
                     CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
-            final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+            final File tracesFile = StackTracesDumpHelper.dumpStackTraces(firstPids,
                     null /* processCpuTracker */, null /* lastPids */,
                     CompletableFuture.completedFuture(nativePids),
                     null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents,
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b67bc62..fb79c067 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -262,14 +262,23 @@
                 if (!isOccluded || prevActivity.canShowWhenLocked()) {
                     // We have another Activity in the same currentTask to go to
                     final WindowContainer parent = currentActivity.getParent();
-                    final boolean isCustomize = parent != null
+                    final boolean canCustomize = parent != null
                             && (parent.asTask() != null
                             || (parent.asTaskFragment() != null
-                            && parent.canCustomizeAppTransition()))
-                            && isCustomizeExitAnimation(window);
-                    if (isCustomize) {
-                        infoBuilder.setWindowAnimations(
-                                window.mAttrs.packageName, window.mAttrs.windowAnimations);
+                            && parent.canCustomizeAppTransition()));
+                    if (canCustomize) {
+                        if (isCustomizeExitAnimation(window)) {
+                            infoBuilder.setWindowAnimations(
+                                    window.mAttrs.packageName, window.mAttrs.windowAnimations);
+                        }
+                        final ActivityRecord.CustomAppTransition customAppTransition =
+                                currentActivity.getCustomAnimation(false/* open */);
+                        if (customAppTransition != null) {
+                            infoBuilder.setCustomAnimation(currentActivity.packageName,
+                                    customAppTransition.mExitAnim,
+                                    customAppTransition.mEnterAnim,
+                                    customAppTransition.mBackgroundColor);
+                        }
                     }
                     removedWindowContainer = currentActivity;
                     prevTask = prevActivity.getTask();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 37ee2e2..1604c2a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1567,7 +1567,14 @@
         if (configChanged) {
             mWaitingForConfig = true;
             if (mTransitionController.isShellTransitionsEnabled()) {
-                requestChangeTransitionIfNeeded(changes, null /* displayChange */);
+                final TransitionRequestInfo.DisplayChange change =
+                        mTransitionController.isCollecting()
+                                ? null : new TransitionRequestInfo.DisplayChange(mDisplayId);
+                if (change != null) {
+                    change.setStartAbsBounds(currentDisplayConfig.windowConfiguration.getBounds());
+                    change.setEndAbsBounds(mTmpConfiguration.windowConfiguration.getBounds());
+                }
+                requestChangeTransitionIfNeeded(changes, change);
             } else if (mLastHasContent) {
                 mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
             }
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index f314b21..7e267e4 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -30,6 +30,7 @@
 import android.app.ActivityManager;
 import android.app.IApplicationThread;
 import android.app.WindowConfiguration;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
@@ -465,6 +466,28 @@
         return type == TRANSIT_OPEN || type == TRANSIT_CLOSE;
     }
 
+    /** Whether the display change should run with blast sync. */
+    private static boolean shouldSync(@NonNull TransitionRequestInfo.DisplayChange displayChange) {
+        if ((displayChange.getStartRotation() + displayChange.getEndRotation()) % 2 == 0) {
+            // 180 degrees rotation change may not change screen size. So the clients may draw
+            // some frames before and after the display projection transaction is applied by the
+            // remote player. That may cause some buffers to show in different rotation. So use
+            // sync method to pause clients drawing until the projection transaction is applied.
+            return true;
+        }
+        final Rect startBounds = displayChange.getStartAbsBounds();
+        final Rect endBounds = displayChange.getEndAbsBounds();
+        if (startBounds == null || endBounds == null) return false;
+        final int startWidth = startBounds.width();
+        final int startHeight = startBounds.height();
+        final int endWidth = endBounds.width();
+        final int endHeight = endBounds.height();
+        // This is changing screen resolution. Because the screen decor layers are excluded from
+        // screenshot, their draw transactions need to run with the start transaction.
+        return (endWidth > startWidth) == (endHeight > startHeight)
+                && (endWidth != startWidth || endHeight != startHeight);
+    }
+
     /**
      * If a transition isn't requested yet, creates one and asks the TransitionPlayer (Shell) to
      * start it. Collection can start immediately.
@@ -494,12 +517,7 @@
         } else {
             newTransition = requestStartTransition(createTransition(type, flags),
                     trigger != null ? trigger.asTask() : null, remoteTransition, displayChange);
-            if (newTransition != null && displayChange != null && (displayChange.getStartRotation()
-                    + displayChange.getEndRotation()) % 2 == 0) {
-                // 180 degrees rotation change may not change screen size. So the clients may draw
-                // some frames before and after the display projection transaction is applied by the
-                // remote player. That may cause some buffers to show in different rotation. So use
-                // sync method to pause clients drawing until the projection transaction is applied.
+            if (newTransition != null && displayChange != null && shouldSync(displayChange)) {
                 mAtm.mWindowManager.mSyncEngine.setSyncMethod(newTransition.getSyncId(),
                         BLASTSyncEngine.METHOD_BLAST);
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d1bd06f..232b817 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2343,6 +2343,9 @@
         // to be removed before the parent (so that the sync-engine tracking works). Since
         // WindowStateAnimator is a "virtual" child, we have to do it manually here.
         mWinAnimator.destroySurfaceLocked(getSyncTransaction());
+        if (!mDrawHandlers.isEmpty()) {
+            mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
+        }
         super.removeImmediately();
 
         final DisplayContent dc = getDisplayContent();
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index a1b9b98..2a256f2 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -80,7 +80,7 @@
 
         final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
         assertThat(state).isNotNull();
-        assertThat(state.hasEdiorFocused()).isTrue();
+        assertThat(state.hasEditorFocused()).isTrue();
         assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
         assertThat(state.isRequestedImeVisible()).isTrue();
 
@@ -95,7 +95,7 @@
 
         final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
         assertThat(state).isNotNull();
-        assertThat(state.hasEdiorFocused()).isTrue();
+        assertThat(state.hasEditorFocused()).isTrue();
         assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
         assertThat(state.isRequestedImeVisible()).isTrue();
 
@@ -113,7 +113,7 @@
 
         final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
         assertThat(state).isNotNull();
-        assertThat(state.hasEdiorFocused()).isTrue();
+        assertThat(state.hasEditorFocused()).isTrue();
         assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
         assertThat(state.isRequestedImeVisible()).isFalse();
 
@@ -131,7 +131,7 @@
 
         final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
         assertThat(state).isNotNull();
-        assertThat(state.hasEdiorFocused()).isTrue();
+        assertThat(state.hasEditorFocused()).isTrue();
         assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
         assertThat(state.isRequestedImeVisible()).isFalse();
 
@@ -149,7 +149,7 @@
 
         final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
         assertThat(state).isNotNull();
-        assertThat(state.hasEdiorFocused()).isTrue();
+        assertThat(state.hasEditorFocused()).isTrue();
         assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
         assertThat(state.isRequestedImeVisible()).isFalse();
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java
index 4d11296..a1937ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java
@@ -53,6 +53,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -69,6 +70,7 @@
     private static final int CURRENT_USER = FakeUserInfoHelper.DEFAULT_USERID;
     private static final String CALLER_PACKAGE = "caller_package";
     private static final String MISSING_PERMISSION = "missing_permission";
+    private static final String ATTRIBUTION_TAG = "test_tag";
 
     private TestInjector mInjector;
     private LocationManagerService mLocationManagerService;
@@ -136,6 +138,7 @@
     }
 
     @Test
+    @Ignore("b/274432939") // Test is flaky for as of yet unknown reasons
     public void testRequestLocationUpdates() {
         LocationRequest request = new LocationRequest.Builder(0).build();
         mLocationManagerService.registerLocationListener(
@@ -143,7 +146,7 @@
                 request,
                 mLocationListener,
                 CALLER_PACKAGE,
-                /* attributionTag= */ null,
+                ATTRIBUTION_TAG,
                 "any_listener_id");
         verify(mProviderWithPermission).onSetRequestPublic(any());
     }
@@ -159,7 +162,7 @@
                                 request,
                                 mLocationListener,
                                 CALLER_PACKAGE,
-                                /* attributionTag= */ null,
+                                ATTRIBUTION_TAG,
                                 "any_listener_id"));
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9b7f244..c4a501d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17914,6 +17914,97 @@
     }
 
     /**
+     * Captures parameters for collection of emergency
+     * call diagnostic data
+     * @hide
+     */
+    public static class EmergencyCallDiagnosticParams {
+
+       private boolean mCollectTelecomDumpSys;
+       private boolean mCollectTelephonyDumpsys;
+       private boolean mCollectLogcat;
+
+        //logcat lines with this time or greater are collected
+        //how much is collected is dependent on internal implementation.
+        //Time represented as milliseconds since January 1, 1970 UTC
+        private long mLogcatStartTimeMillis;
+
+
+        public boolean isTelecomDumpSysCollectionEnabled() {
+            return mCollectTelecomDumpSys;
+        }
+
+        public void setTelecomDumpSysCollection(boolean collectTelecomDumpSys) {
+            mCollectTelecomDumpSys = collectTelecomDumpSys;
+        }
+
+        public boolean isTelephonyDumpSysCollectionEnabled() {
+            return mCollectTelephonyDumpsys;
+        }
+
+        public void setTelephonyDumpSysCollection(boolean collectTelephonyDumpsys) {
+            mCollectTelephonyDumpsys = collectTelephonyDumpsys;
+        }
+
+        public boolean isLogcatCollectionEnabled() {
+            return mCollectLogcat;
+        }
+
+        public long getLogcatStartTime()
+        {
+            return mLogcatStartTimeMillis;
+        }
+
+        public void setLogcatCollection(boolean collectLogcat, long startTimeMillis) {
+            mCollectLogcat = collectLogcat;
+            if(mCollectLogcat)
+            {
+                mLogcatStartTimeMillis = startTimeMillis;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "EmergencyCallDiagnosticParams{" +
+                    "mCollectTelecomDumpSys=" + mCollectTelecomDumpSys +
+                    ", mCollectTelephonyDumpsys=" + mCollectTelephonyDumpsys +
+                    ", mCollectLogcat=" + mCollectLogcat +
+                    ", mLogcatStartTimeMillis=" + mLogcatStartTimeMillis +
+                    '}';
+        }
+    }
+
+    /**
+     * Request telephony to persist state for debugging emergency call failures.
+     *
+     * @param dropboxTag Tag to use when persisting data to dropbox service.
+     *
+     * @see params Parameters controlling what is collected
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.DUMP)
+    public void persistEmergencyCallDiagnosticData(@NonNull String dropboxTag,
+            @NonNull EmergencyCallDiagnosticParams params) {
+        try {
+            ITelephony telephony = ITelephony.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getTelephonyServiceRegisterer()
+                            .get());
+            if (telephony != null) {
+                telephony.persistEmergencyCallDiagnosticData(dropboxTag,
+                        params.isLogcatCollectionEnabled(),
+                        params.getLogcatStartTime(),
+                        params.isTelecomDumpSysCollectionEnabled(),
+                        params.isTelephonyDumpSysCollectionEnabled());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error while persistEmergencyCallDiagnosticData: " + e);
+        }
+    }
+
+    /**
      * Set the UE's ability to accept/reject null ciphered and null integrity-protected connections.
      *
      * The modem is required to ignore this in case of an emergency call.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d0de3ac..bab08b5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2677,6 +2677,21 @@
     int getSimStateForSlotIndex(int slotIndex);
 
     /**
+     * Request telephony to persist state for debugging emergency call failures.
+     *
+     * @param dropBoxTag Tag to use when persisting data to dropbox service.
+     * @param enableLogcat whether to collect logcat output
+     * @param logcatStartTimestampMillis timestamp from when logcat buffers would be persisted
+     * @param enableTelecomDump whether to collect telecom dumpsys
+     * @param enableTelephonyDump whether to collect telephony dumpsys
+     *
+     * @hide
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.DUMP)")
+    void persistEmergencyCallDiagnosticData(String dropboxTag, boolean enableLogcat,
+        long logcatStartTimestampMillis, boolean enableTelecomDump, boolean enableTelephonyDump);
+    /**
      * Set whether the radio is able to connect with null ciphering or integrity
      * algorithms. This is a global setting and will apply to all active subscriptions
      * and all new subscriptions after this.