Merge "[Media TTT] Don't show scrubbing times if we don't have the prev or next button." into tm-dev am: 7af8cbc481 am: 394937fdfa am: c9f3453b42

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17638186

Change-Id: I042b51d1613e55b5a88726fac1c6d4be928ba053
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index df6fdaa..6892205 100644
--- a/Android.bp
+++ b/Android.bp
@@ -265,6 +265,60 @@
     ],
     aidl: {
         generate_get_transaction_name: true,
+        enforce_permissions: true,
+        enforce_permissions_exceptions: [
+            // Do not add entries to this list.
+            ":framework-annotations",
+            ":framework-blobstore-sources",
+            ":framework-core-sources",
+            ":framework-drm-sources",
+            ":framework-graphics-nonupdatable-sources",
+            ":framework-jobscheduler-sources",
+            ":framework-keystore-sources",
+            ":framework-identity-sources",
+            ":framework-location-sources",
+            ":framework-lowpan-sources",
+            ":framework-mca-effect-sources",
+            ":framework-mca-filterfw-sources",
+            ":framework-mca-filterpacks-sources",
+            ":framework-media-non-updatable-sources",
+            ":framework-mms-sources",
+            ":framework-omapi-sources",
+            ":framework-opengl-sources",
+            ":framework-rs-sources",
+            ":framework-sax-sources",
+            ":framework-telecomm-sources",
+            ":framework-telephony-common-sources",
+            ":framework-telephony-sources",
+            ":framework-vcn-util-sources",
+            ":framework-wifi-annotations",
+            ":framework-wifi-non-updatable-sources",
+            ":PacProcessor-aidl-sources",
+            ":ProxyHandler-aidl-sources",
+            ":net-utils-framework-common-srcs",
+            ":platform-compat-native-aidl",
+            ":credstore_aidl",
+            ":dumpstate_aidl",
+            ":framework_native_aidl",
+            ":gatekeeper_aidl",
+            ":gsiservice_aidl",
+            ":idmap2_aidl",
+            ":idmap2_core_aidl",
+            ":incidentcompanion_aidl",
+            ":inputconstants_aidl",
+            ":installd_aidl",
+            ":libaudioclient_aidl",
+            ":libbinder_aidl",
+            ":libbluetooth-binder-aidl",
+            ":libcamera_client_aidl",
+            ":libcamera_client_framework_aidl",
+            ":libupdate_engine_aidl",
+            ":logd_aidl",
+            ":resourcemanager_aidl",
+            ":storaged_aidl",
+            ":vold_aidl",
+            ":deviceproductinfoconstants_aidl",
+        ],
         local_include_dirs: [
             "media/aidl",
         ],
diff --git a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
index 4cd9741..086e185 100644
--- a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
+import android.util.DisplayMetrics;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 
@@ -59,10 +60,13 @@
     public void setup() {
         final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mContext = mInstrumentation.getTargetContext();
-        ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext);
+        final ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext);
         mTouchSlop = viewConfiguration.getScaledTouchSlop();
-        InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
-        mHandwritingInitiator = new HandwritingInitiator(viewConfiguration, inputMethodManager);
+        final InputMethodManager inputMethodManager =
+                mContext.getSystemService(InputMethodManager.class);
+        final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+        mHandwritingInitiator = new HandwritingInitiator(viewConfiguration, inputMethodManager,
+                displayMetrics);
     }
 
     @Test
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index b4238c9..c8ec894 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1208,7 +1208,7 @@
             }
             if (highestBiasJob != null) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Running job " + jobStatus + " as preemption");
+                    Slog.d(TAG, "Running job " + highestBiasJob + " as preemption");
                 }
                 mWorkCountTracker.stageJob(highBiasWorkType, highBiasAllWorkTypes);
                 startJobLocked(worker, highestBiasJob, highBiasWorkType);
@@ -1219,7 +1219,7 @@
                 worker.clearPreferredUid();
                 if (backupJob != null) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Running job " + jobStatus + " instead");
+                        Slog.d(TAG, "Running job " + backupJob + " instead");
                     }
                     mWorkCountTracker.stageJob(backupWorkType, backupAllWorkTypes);
                     startJobLocked(worker, backupJob, backupWorkType);
@@ -1263,7 +1263,7 @@
                 // This slot is free, and we haven't yet hit the limit on
                 // concurrent jobs...  we can just throw the job in to here.
                 if (DEBUG) {
-                    Slog.d(TAG, "About to run job: " + jobStatus);
+                    Slog.d(TAG, "About to run job: " + highestBiasJob);
                 }
                 mWorkCountTracker.stageJob(highBiasWorkType, highBiasAllWorkTypes);
                 startJobLocked(worker, highestBiasJob, highBiasWorkType);
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 6ef6845..18ec5a4 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -23,6 +23,7 @@
 
 cc_defaults {
     name: "idmap2_defaults",
+    cpp_std: "gnu++2b",
     tidy: true,
     tidy_checks: [
         "modernize-*",
@@ -31,6 +32,7 @@
         "android-*",
         "misc-*",
         "readability-*",
+        "-readability-identifier-length",
     ],
     tidy_checks_as_errors: [
         "modernize-*",
@@ -54,7 +56,6 @@
         "-readability-convert-member-functions-to-static",
         "-readability-duplicate-include",
         "-readability-else-after-return",
-        "-readability-identifier-length",
         "-readability-named-parameter",
         "-readability-redundant-access-specifiers",
         "-readability-uppercase-literal-suffix",
@@ -115,6 +116,7 @@
         "libidmap2/proto/*.proto",
     ],
     host_supported: true,
+    tidy: false,
     proto: {
         type: "lite",
         export_proto_headers: true,
diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp
index f2f8854..f9c4fa3 100644
--- a/cmds/idmap2/tests/ResultTests.cpp
+++ b/cmds/idmap2/tests/ResultTests.cpp
@@ -259,7 +259,8 @@
 }
 
 struct NoCopyContainer {
-  uint32_t value;  // NOLINT(misc-non-private-member-variables-in-classes)
+  uint32_t value = 0;  // NOLINT(misc-non-private-member-variables-in-classes)
+  NoCopyContainer() = default;
   NoCopyContainer(const NoCopyContainer&) = delete;
   NoCopyContainer& operator=(const NoCopyContainer&) = delete;
 };
@@ -268,7 +269,7 @@
   if (!succeed) {
     return Error("foo");
   }
-  std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{0U});
+  std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{});
   p->value = 42U;
   return std::move(p);
 }
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 52f883b..50c2e75 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -52,7 +52,7 @@
 
       (new Telecom()).run(args);
     }
-
+    private static final String CALLING_PACKAGE = Telecom.class.getPackageName();
     private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
     private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
     private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
@@ -286,7 +286,7 @@
         final String label = nextArgRequired();
         PhoneAccount account = PhoneAccount.builder(handle, label)
                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
-        mTelecomService.registerPhoneAccount(account);
+        mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE);
         System.out.println("Success - " + handle + " registered.");
     }
 
@@ -316,7 +316,7 @@
                 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
                 .build();
-        mTelecomService.registerPhoneAccount(account);
+        mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE);
         System.out.println("Success - " + handle + " registered.");
     }
 
@@ -358,7 +358,7 @@
 
     private void runUnregisterPhoneAccount() throws RemoteException {
         final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
-        mTelecomService.unregisterPhoneAccount(handle);
+        mTelecomService.unregisterPhoneAccount(handle, CALLING_PACKAGE);
         System.out.println("Success - " + handle + " unregistered.");
     }
 
@@ -395,11 +395,11 @@
     }
 
     private void runGetDefaultDialer() throws RemoteException {
-        System.out.println(mTelecomService.getDefaultDialerPackage());
+        System.out.println(mTelecomService.getDefaultDialerPackage(CALLING_PACKAGE));
     }
 
     private void runGetSystemDialer() throws RemoteException {
-        System.out.println(mTelecomService.getSystemDialerPackage());
+        System.out.println(mTelecomService.getSystemDialerPackage(CALLING_PACKAGE));
     }
 
     private void runWaitOnHandler() throws RemoteException {
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
index 06fa2aa..3f4163d 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
@@ -99,6 +99,7 @@
 
 std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vid,
                                                  int32_t pid, uint16_t bus, uint32_t ffEffectsMax,
+                                                 const char* port,
                                                  std::unique_ptr<DeviceCallback> callback) {
     android::base::unique_fd fd(::open(UINPUT_PATH, O_RDWR | O_NONBLOCK | O_CLOEXEC));
     if (!fd.ok()) {
@@ -131,6 +132,9 @@
         return nullptr;
     }
 
+    // set the physical port.
+    ::ioctl(fd, UI_SET_PHYS, port);
+
     if (::ioctl(fd, UI_DEV_CREATE) != 0) {
         ALOGE("Unable to create uinput device: %s.", strerror(errno));
         return nullptr;
@@ -240,17 +244,19 @@
 }
 
 static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
-                              jint pid, jint bus, jint ffEffectsMax, jobject callback) {
+                              jint pid, jint bus, jint ffEffectsMax, jstring rawPort,
+                              jobject callback) {
     ScopedUtfChars name(env, rawName);
     if (name.c_str() == nullptr) {
         return 0;
     }
 
+    ScopedUtfChars port(env, rawPort);
     std::unique_ptr<uinput::DeviceCallback> cb =
             std::make_unique<uinput::DeviceCallback>(env, callback);
 
     std::unique_ptr<uinput::UinputDevice> d =
-            uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax,
+            uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax, port.c_str(),
                                        std::move(cb));
     return reinterpret_cast<jlong>(d.release());
 }
@@ -303,7 +309,7 @@
 
 static JNINativeMethod sMethods[] = {
         {"nativeOpenUinputDevice",
-         "(Ljava/lang/String;IIIII"
+         "(Ljava/lang/String;IIIIILjava/lang/String;"
          "Lcom/android/commands/uinput/Device$DeviceCallback;)J",
          reinterpret_cast<void*>(openUinputDevice)},
         {"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)},
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.h b/cmds/uinput/jni/com_android_commands_uinput_Device.h
index 5a9a06c..6da3d79 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.h
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.h
@@ -48,6 +48,7 @@
 public:
     static std::unique_ptr<UinputDevice> open(int32_t id, const char* name, int32_t vid,
                                               int32_t pid, uint16_t bus, uint32_t ff_effects_max,
+                                              const char* port,
                                               std::unique_ptr<DeviceCallback> callback);
 
     virtual ~UinputDevice();
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
index 62bee7b..732b33d 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Device.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -61,7 +61,7 @@
     }
 
     private static native long nativeOpenUinputDevice(String name, int id, int vid, int pid,
-            int bus, int ffEffectsMax, DeviceCallback callback);
+            int bus, int ffEffectsMax, String port, DeviceCallback callback);
     private static native void nativeCloseUinputDevice(long ptr);
     private static native void nativeInjectEvent(long ptr, int type, int code, int value);
     private static native void nativeConfigure(int handle, int code, int[] configs);
@@ -69,7 +69,7 @@
 
     public Device(int id, String name, int vid, int pid, int bus,
             SparseArray<int[]> configuration, int ffEffectsMax,
-            SparseArray<InputAbsInfo> absInfo) {
+            SparseArray<InputAbsInfo> absInfo, String port) {
         mId = id;
         mThread = new HandlerThread("UinputDeviceHandler");
         mThread.start();
@@ -88,6 +88,11 @@
         } else {
             args.arg1 = id + ":" + vid + ":" + pid;
         }
+        if (port != null) {
+            args.arg2 = port;
+        } else {
+            args.arg2 = "uinput:" + id + ":" + vid + ":" + pid;
+        }
 
         mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget();
         mTimeToSend = SystemClock.uptimeMillis();
@@ -142,7 +147,7 @@
                 case MSG_OPEN_UINPUT_DEVICE:
                     SomeArgs args = (SomeArgs) msg.obj;
                     mPtr = nativeOpenUinputDevice((String) args.arg1, args.argi1, args.argi2,
-                            args.argi3, args.argi4, args.argi5,
+                            args.argi3, args.argi4, args.argi5, (String) args.arg2,
                             new DeviceCallback());
                     break;
                 case MSG_INJECT_EVENT:
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index c4ba050..9add310e 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -64,6 +64,7 @@
     private SparseArray<int[]> mConfiguration;
     private int mDuration;
     private int mFfEffectsMax = 0;
+    private String mInputport;
     private SparseArray<InputAbsInfo> mAbsInfo;
 
     public int getId() {
@@ -110,6 +111,10 @@
         return mAbsInfo;
     }
 
+    public String getPort() {
+        return mInputport;
+    }
+
     /**
      * Convert an event to String.
      */
@@ -124,6 +129,7 @@
             + ", configuration=" + mConfiguration
             + ", duration=" + mDuration
             + ", ff_effects_max=" + mFfEffectsMax
+            + ", port=" + mInputport
             + "}";
     }
 
@@ -178,6 +184,10 @@
             mEvent.mAbsInfo = absInfo;
         }
 
+        public void setInputport(String port) {
+            mEvent.mInputport = port;
+        }
+
         public Event build() {
             if (mEvent.mId == -1) {
                 throw new IllegalStateException("No event id");
@@ -262,6 +272,9 @@
                             case "duration":
                                 eb.setDuration(readInt());
                                 break;
+                            case "port":
+                                eb.setInputport(mReader.nextString());
+                                break;
                             default:
                                 mReader.skipValue();
                         }
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index f7601a2..740578e 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -123,7 +123,7 @@
         }
         int id = e.getId();
         Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
-                e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo());
+                e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo(), e.getPort());
         mDevices.append(id, d);
     }
 
diff --git a/core/api/current.txt b/core/api/current.txt
index b08ffc9..c5ab512 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8996,6 +8996,7 @@
   public final class CompanionDeviceManager {
     method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.Callback);
+    method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException;
     method @Deprecated public void disassociate(@NonNull String);
     method public void disassociate(int);
     method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations();
@@ -9003,6 +9004,7 @@
     method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
     method public void requestNotificationAccess(android.content.ComponentName);
     method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
+    method public void startSystemDataTransfer(int) throws android.companion.DeviceNotAssociatedException;
     method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
     field public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION";
     field @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
@@ -9018,11 +9020,13 @@
 
   public abstract class CompanionDeviceService extends android.app.Service {
     ctor public CompanionDeviceService();
+    method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void dispatchMessageToSystem(int, int, @NonNull byte[]) throws android.companion.DeviceNotAssociatedException;
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method @Deprecated @MainThread public void onDeviceAppeared(@NonNull String);
     method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo);
     method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String);
     method @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo);
+    method public void onMessageDispatchedFromSystem(int, int, @NonNull byte[]);
     field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService";
   }
 
@@ -18526,7 +18530,7 @@
 package android.hardware.input {
 
   public final class InputManager {
-    method public android.view.InputDevice getInputDevice(int);
+    method @Nullable public android.view.InputDevice getInputDevice(int);
     method public int[] getInputDeviceIds();
     method @FloatRange(from=0, to=1) public float getMaximumObscuringOpacityForTouch();
     method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
@@ -20081,14 +20085,14 @@
     method @Deprecated public int getVibrateSetting(int);
     method @Deprecated public boolean isBluetoothA2dpOn();
     method public boolean isBluetoothScoAvailableOffCall();
-    method public boolean isBluetoothScoOn();
+    method @Deprecated public boolean isBluetoothScoOn();
     method public boolean isCallScreeningModeSupported();
     method public static boolean isHapticPlaybackSupported();
     method public boolean isMicrophoneMute();
     method public boolean isMusicActive();
     method public static boolean isOffloadedPlaybackSupported(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
     method public boolean isRampingRingerEnabled();
-    method public boolean isSpeakerphoneOn();
+    method @Deprecated public boolean isSpeakerphoneOn();
     method public boolean isStreamMute(int);
     method public boolean isSurroundFormatEnabled(int);
     method public boolean isVolumeFixed();
@@ -20117,7 +20121,7 @@
     method public void setParameters(String);
     method public void setRingerMode(int);
     method @Deprecated public void setRouting(int, int, int);
-    method public void setSpeakerphoneOn(boolean);
+    method @Deprecated public void setSpeakerphoneOn(boolean);
     method @Deprecated public void setStreamMute(int, boolean);
     method @Deprecated public void setStreamSolo(int, boolean);
     method public void setStreamVolume(int, int, int);
@@ -20125,8 +20129,8 @@
     method @Deprecated public void setVibrateSetting(int, int);
     method @Deprecated public void setWiredHeadsetOn(boolean);
     method @Deprecated public boolean shouldVibrate(int);
-    method public void startBluetoothSco();
-    method public void stopBluetoothSco();
+    method @Deprecated public void startBluetoothSco();
+    method @Deprecated public void stopBluetoothSco();
     method public void unloadSoundEffects();
     method public void unregisterAudioDeviceCallback(android.media.AudioDeviceCallback);
     method public void unregisterAudioPlaybackCallback(@NonNull android.media.AudioManager.AudioPlaybackCallback);
@@ -32341,6 +32345,9 @@
     method public static android.os.VibrationEffect createWaveform(long[], int[], int);
     method public int describeContents();
     method @NonNull public static android.os.VibrationEffect.Composition startComposition();
+    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform();
+    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter);
+    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter);
     field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect> CREATOR;
     field public static final int DEFAULT_AMPLITUDE = -1; // 0xffffffff
     field public static final int EFFECT_CLICK = 0; // 0x0
@@ -32350,10 +32357,13 @@
   }
 
   public static final class VibrationEffect.Composition {
+    method @NonNull public android.os.VibrationEffect.Composition addEffect(@NonNull android.os.VibrationEffect);
+    method @NonNull public android.os.VibrationEffect.Composition addOffDuration(@NonNull java.time.Duration);
     method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int);
     method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float);
     method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
     method @NonNull public android.os.VibrationEffect compose();
+    method @NonNull public android.os.VibrationEffect.Composition repeatEffectIndefinitely(@NonNull android.os.VibrationEffect);
     field public static final int PRIMITIVE_CLICK = 1; // 0x1
     field public static final int PRIMITIVE_LOW_TICK = 8; // 0x8
     field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6
@@ -32364,15 +32374,34 @@
     field public static final int PRIMITIVE_TICK = 7; // 0x7
   }
 
+  public static final class VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException extends java.lang.IllegalStateException {
+  }
+
+  public static class VibrationEffect.VibrationParameter {
+    method @NonNull public static android.os.VibrationEffect.VibrationParameter targetAmplitude(@FloatRange(from=0, to=1) float);
+    method @NonNull public static android.os.VibrationEffect.VibrationParameter targetFrequency(@FloatRange(from=1) float);
+  }
+
+  public static final class VibrationEffect.WaveformBuilder {
+    method @NonNull public android.os.VibrationEffect.WaveformBuilder addSustain(@NonNull java.time.Duration);
+    method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter);
+    method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter);
+    method @NonNull public android.os.VibrationEffect build();
+  }
+
   public abstract class Vibrator {
     method public final int areAllEffectsSupported(@NonNull int...);
     method public final boolean areAllPrimitivesSupported(@NonNull int...);
     method @NonNull public int[] areEffectsSupported(@NonNull int...);
     method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
     method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
+    method @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile();
     method public int getId();
     method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
+    method public float getQFactor();
+    method public float getResonantFrequency();
     method public abstract boolean hasAmplitudeControl();
+    method public boolean hasFrequencyControl();
     method public abstract boolean hasVibrator();
     method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
     method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long, android.media.AudioAttributes);
@@ -32700,6 +32729,17 @@
 
 }
 
+package android.os.vibrator {
+
+  public final class VibratorFrequencyProfile {
+    method public float getMaxAmplitudeMeasurementInterval();
+    method @FloatRange(from=0, to=1) @NonNull public float[] getMaxAmplitudeMeasurements();
+    method public float getMaxFrequency();
+    method public float getMinFrequency();
+  }
+
+}
+
 package android.preference {
 
   @Deprecated public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -44254,6 +44294,9 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
     method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
     field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
+    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
   }
 
   public final class ImsReasonInfo implements android.os.Parcelable {
@@ -44475,10 +44518,10 @@
     method public void unregisterFeatureProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback);
   }
 
-  public static class ProvisioningManager.FeatureProvisioningCallback {
+  public abstract static class ProvisioningManager.FeatureProvisioningCallback {
     ctor public ProvisioningManager.FeatureProvisioningCallback();
-    method public void onFeatureProvisioningChanged(int, int, boolean);
-    method public void onRcsFeatureProvisioningChanged(int, int, boolean);
+    method public abstract void onFeatureProvisioningChanged(int, int, boolean);
+    method public abstract void onRcsFeatureProvisioningChanged(int, int, boolean);
   }
 
   public class RcsUceAdapter {
@@ -44521,15 +44564,6 @@
     field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
   }
 
-  public class RcsFeature {
-  }
-
-  public static class RcsFeature.RcsImsCapabilities {
-    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
-    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
-    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
-  }
-
 }
 
 package android.telephony.ims.stub {
@@ -48132,7 +48166,7 @@
     method @NonNull public android.hardware.BatteryState getBatteryState();
     method public int getControllerNumber();
     method public String getDescriptor();
-    method public static android.view.InputDevice getDevice(int);
+    method @Nullable public static android.view.InputDevice getDevice(int);
     method public static int[] getDeviceIds();
     method public int getId();
     method public android.view.KeyCharacterMap getKeyCharacterMap();
@@ -52512,6 +52546,7 @@
 
   public final class AutofillManager {
     method public void cancel();
+    method public void clearAutofillRequestCallback();
     method public void commit();
     method public void disableAutofillServices();
     method @Nullable public android.content.ComponentName getAutofillServiceComponentName();
@@ -52537,6 +52572,7 @@
     method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
     method public void requestAutofill(@NonNull android.view.View);
     method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
+    method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
     method public void setUserData(@Nullable android.service.autofill.UserData);
     method public boolean showAutofillDialog(@NonNull android.view.View);
     method public boolean showAutofillDialog(@NonNull android.view.View, int);
@@ -52557,6 +52593,10 @@
     field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
   }
 
+  public interface AutofillRequestCallback {
+    method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
+  }
+
   public final class AutofillValue implements android.os.Parcelable {
     method public int describeContents();
     method public static android.view.autofill.AutofillValue forDate(long);
@@ -52944,10 +52984,12 @@
     ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
+    method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
+    method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
   }
 
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b952216..e81d7f1 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -2,6 +2,7 @@
 package android {
 
   public static final class Manifest.permission {
+    field public static final String BLUETOOTH_STACK = "android.permission.BLUETOOTH_STACK";
     field public static final String CONTROL_AUTOMOTIVE_GNSS = "android.permission.CONTROL_AUTOMOTIVE_GNSS";
     field public static final String GET_INTENT_SENDER_INTENT = "android.permission.GET_INTENT_SENDER_INTENT";
     field public static final String MAKE_UID_VISIBLE = "android.permission.MAKE_UID_VISIBLE";
@@ -156,12 +157,12 @@
     method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
     method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp();
     method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio();
-    method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo);
-    method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean);
-    method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean);
-    method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean);
-    method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpSamplingRate(int);
-    method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpVolume(int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setA2dpSuspended(boolean);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setHfpEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setHfpSamplingRate(int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void setHfpVolume(int);
     method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 650de2e0..03b4aa5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -199,6 +199,7 @@
     field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN";
     field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
     field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED";
+    field public static final String MEDIA_RESOURCE_OVERRIDE_PID = "android.permission.MEDIA_RESOURCE_OVERRIDE_PID";
     field public static final String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
     field public static final String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
     field public static final String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
@@ -1050,7 +1051,7 @@
 
   public class WallpaperManager {
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int);
-    method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
+    method @FloatRange(from=0.0f, to=1.0f) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
     method public void setDisplayOffset(android.os.IBinder, int, int);
     method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName);
     method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange(from=0.0f, to=1.0f) float);
@@ -6334,7 +6335,7 @@
   }
 
   public final class MediaCodec {
-    method @NonNull @RequiresPermission("android.permission.MEDIA_RESOURCE_OVERRIDE_PID") public static android.media.MediaCodec createByCodecNameForClient(@NonNull String, int, int) throws java.io.IOException;
+    method @NonNull @RequiresPermission(android.Manifest.permission.MEDIA_RESOURCE_OVERRIDE_PID) public static android.media.MediaCodec createByCodecNameForClient(@NonNull String, int, int) throws java.io.IOException;
   }
 
   public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
@@ -11873,19 +11874,20 @@
 package android.service.voice {
 
   public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
-    method @Nullable public android.content.Intent createEnrollIntent();
-    method @Nullable public android.content.Intent createReEnrollIntent();
-    method @Nullable public android.content.Intent createUnEnrollIntent();
-    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int);
+    method @Nullable public android.content.Intent createEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @Nullable public android.content.Intent createReEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @Nullable public android.content.Intent createUnEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
     method public int getSupportedAudioCapabilities();
-    method public int getSupportedRecognitionModes();
-    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
-    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
-    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
-    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
-    method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
-    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
-    method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
+    method public int getSupportedRecognitionModes() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int, @NonNull byte[]) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
     field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
     field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
     field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
@@ -11992,10 +11994,10 @@
 
   public interface HotwordDetector {
     method public default void destroy();
-    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
-    method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
-    method public boolean stopRecognition();
-    method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
+    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+    method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
   }
 
   public static interface HotwordDetector.Callback {
@@ -12008,6 +12010,9 @@
     method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
   }
 
+  public static class HotwordDetector.IllegalDetectorStateException extends android.util.AndroidException {
+  }
+
   public final class HotwordRejectedResult implements android.os.Parcelable {
     method public int describeContents();
     method public int getConfidenceLevel();
@@ -15024,7 +15029,7 @@
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
-    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+    field @Deprecated public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
     field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
     field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
     field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
@@ -15307,6 +15312,9 @@
     method public void addCapabilities(int);
     method public boolean isCapable(int);
     method public void removeCapabilities(int);
+    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
   }
 
 }
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 1b45e88..db375d4 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -99,15 +99,6 @@
     
 
 
-RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimAmount():
-    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
-RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimmingAmount():
-    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
-RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimAmount(float):
-    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
-RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimmingAmount(float):
-    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
-
 
 SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
     
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 27f5b92..37edc03 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -36,7 +36,6 @@
     field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
     field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
-    field public static final String REQUEST_UNIQUE_ID_ATTESTATION = "android.permission.REQUEST_UNIQUE_ID_ATTESTATION";
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
     field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL";
     field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
@@ -1250,10 +1249,12 @@
   }
 
   public final class InputManager {
+    method public void addUniqueIdAssociation(@NonNull String, @NonNull String);
     method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context);
     method @Nullable public String getCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier);
     method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull android.view.InputDevice);
     method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
+    method public void removeUniqueIdAssociation(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int);
     method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void setCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@FloatRange(from=0, to=1) float);
@@ -1857,7 +1858,6 @@
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
-    method public static boolean isGuestUserEphemeral();
     method public static boolean isSplitSystemUser();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
   }
@@ -1871,9 +1871,6 @@
     method public static android.os.VibrationEffect get(int, boolean);
     method @Nullable public static android.os.VibrationEffect get(android.net.Uri, android.content.Context);
     method public abstract long getDuration();
-    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform();
-    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter);
-    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter);
     field public static final int EFFECT_POP = 4; // 0x4
     field public static final int EFFECT_STRENGTH_LIGHT = 0; // 0x0
     field public static final int EFFECT_STRENGTH_MEDIUM = 1; // 0x1
@@ -1891,33 +1888,8 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Composed> CREATOR;
   }
 
-  public static final class VibrationEffect.Composition {
-    method @NonNull public android.os.VibrationEffect.Composition addEffect(@NonNull android.os.VibrationEffect);
-    method @NonNull public android.os.VibrationEffect.Composition addOffDuration(@NonNull java.time.Duration);
-    method @NonNull public android.os.VibrationEffect.Composition repeatEffectIndefinitely(@NonNull android.os.VibrationEffect);
-  }
-
-  public static final class VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException extends java.lang.IllegalStateException {
-  }
-
-  public static class VibrationEffect.VibrationParameter {
-    method @NonNull public static android.os.VibrationEffect.VibrationParameter targetAmplitude(@FloatRange(from=0, to=1) float);
-    method @NonNull public static android.os.VibrationEffect.VibrationParameter targetFrequency(@FloatRange(from=1) float);
-  }
-
-  public static final class VibrationEffect.WaveformBuilder {
-    method @NonNull public android.os.VibrationEffect.WaveformBuilder addSustain(@NonNull java.time.Duration);
-    method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter);
-    method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter);
-    method @NonNull public android.os.VibrationEffect build();
-  }
-
   public abstract class Vibrator {
     method public int getDefaultVibrationIntensity(int);
-    method @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile();
-    method public float getQFactor();
-    method public float getResonantFrequency();
-    method public boolean hasFrequencyControl();
     field public static final int VIBRATION_INTENSITY_HIGH = 3; // 0x3
     field public static final int VIBRATION_INTENSITY_LOW = 1; // 0x1
     field public static final int VIBRATION_INTENSITY_MEDIUM = 2; // 0x2
@@ -2102,13 +2074,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.VibrationEffectSegment> CREATOR;
   }
 
-  public final class VibratorFrequencyProfile {
-    method public float getMaxAmplitudeMeasurementInterval();
-    method @FloatRange(from=0, to=1) @NonNull public float[] getMaxAmplitudeMeasurements();
-    method public float getMaxFrequency();
-    method public float getMinFrequency();
-  }
-
 }
 
 package android.permission {
@@ -2467,6 +2432,8 @@
 package android.service.voice {
 
   public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
+    method public void overrideAvailability(int);
+    method public void resetAvailability();
     method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], @NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
   }
 
@@ -2826,6 +2793,7 @@
     method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
     method @Nullable public android.view.Display.Mode getSystemPreferredDisplayMode();
     method public int getType();
+    method @Nullable public String getUniqueId();
     method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
     method public boolean hasAccess(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index e6cdcc0..0d6a079 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -31,7 +31,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 
-import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -87,12 +86,6 @@
         if (TextUtils.isEmpty(type)) {
             throw new IllegalArgumentException("the type must not be empty: " + type);
         }
-        if (name.length() > 200) {
-            throw new IllegalArgumentException("account name is longer than 200 characters");
-        }
-        if (type.length() > 200) {
-            throw new IllegalArgumentException("account type is longer than 200 characters");
-        }
         this.name = name;
         this.type = type;
         this.accessId = accessId;
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 28c273e..167de46 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -211,6 +211,7 @@
      *
      * @hide
      */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)")
     oneway void setWallpaperDimAmount(float dimAmount);
 
     /**
@@ -219,6 +220,7 @@
      *
      * @hide
      */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)")
     float getWallpaperDimAmount();
 
     /**
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 649f904..3c64b99 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -110,6 +110,19 @@
             "options": [
                 {
                     "exclude-annotation": "androidx.test.filters.FlakyTest"
+                },
+                // TODO(b/225076204): Remove the following four test cases after fixing the test fail.
+                {
+                    "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_createDetectorTwiceQuickly_triggerSuccess"
+                },
+                {
+                    "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromExternalSource_success"
+                },
+                {
+                    "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_createDetectorTwiceQuickly_triggerSuccess"
+                },
+                {
+                    "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromMic_success"
                 }
             ],
             "file_patterns": ["(/|^)VoiceInteract[^/]*"]
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 5d6e2bd..7964ae9 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -431,8 +431,7 @@
                     writeTo.write(buffer, 0, readByteCount);
                     writeTo.flush();
                 }
-            } catch (IOException ioe) {
-                Log.w(TAG, "Error while reading/writing to streams");
+            } catch (IOException ignored) {
             } finally {
                 IoUtils.closeQuietly(readFrom);
                 IoUtils.closeQuietly(writeTo);
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 0a18588..ea80369 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2023,7 +2023,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)
-    public float getWallpaperDimAmount() {
+    public @FloatRange (from = 0f, to = 1f) float getWallpaperDimAmount() {
         if (sGlobals.mService == null) {
             Log.w(TAG, "WallpaperService not running");
             throw new RuntimeException(new DeadSystemException());
diff --git a/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS b/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS
index f560434..0ec8253 100644
--- a/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS
+++ b/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS
@@ -1,4 +1,5 @@
 rubinxu@google.com
 acjohnston@google.com
 pgrafov@google.com
+ayushsha@google.com
 alexkershaw@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/core/java/android/app/admin/WorkDeviceExperience_OWNERS b/core/java/android/app/admin/WorkDeviceExperience_OWNERS
index dcacaa2..7c90feb 100644
--- a/core/java/android/app/admin/WorkDeviceExperience_OWNERS
+++ b/core/java/android/app/admin/WorkDeviceExperience_OWNERS
@@ -2,4 +2,5 @@
 scottjonathan@google.com
 arangelov@google.com
 kholoudm@google.com
+eliselliott@google.com
 alexkershaw@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 67f631f..88a7c0f 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -246,6 +246,9 @@
      * new changes to its data.  A backup operation using your application's
      * {@link android.app.backup.BackupAgent} subclass will be scheduled when you
      * call this method.
+     *
+     * <p>
+     * Note: This only works if your application is performing Key/Value backups.
      */
     public void dataChanged() {
         checkServiceBinder();
@@ -268,6 +271,8 @@
      * as the caller.
      *
      * @param packageName The package name identifying the application to back up.
+     * <p>
+     * Note: Only works for packages performing Key/Value backups.
      */
     public static void dataChanged(String packageName) {
         checkServiceBinder();
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 6f73d02..f31e2bc 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -33,7 +33,7 @@
     // Perms.
     void grantSlicePermission(String callingPkg, String toPkg, in Uri uri);
     void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri);
-    int checkSlicePermission(in Uri uri, String callingPkg, String pkg, int pid, int uid,
+    int checkSlicePermission(in Uri uri, String callingPkg, int pid, int uid,
             in String[] autoGrantPermissions);
     void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
 }
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 5497b78..ed4ea74 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -439,8 +439,8 @@
      */
     public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
         try {
-            return mService.checkSlicePermission(uri, mContext.getPackageName(), null, pid, uid,
-                    null);
+            return mService.checkSlicePermission(uri, mContext.getPackageName(), pid, uid,
+                    null /* autoGrantPermissions */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -488,17 +488,13 @@
      * Does the permission check to see if a caller has access to a specific slice.
      * @hide
      */
-    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid,
-            String[] autoGrantPermissions) {
+    public void enforceSlicePermission(Uri uri, int pid, int uid, String[] autoGrantPermissions) {
         try {
             if (UserHandle.isSameApp(uid, Process.myUid())) {
                 return;
             }
-            if (pkg == null) {
-                throw new SecurityException("No pkg specified");
-            }
-            int result = mService.checkSlicePermission(uri, mContext.getPackageName(), pkg, pid,
-                    uid, autoGrantPermissions);
+            int result = mService.checkSlicePermission(uri, mContext.getPackageName(), pid, uid,
+                    autoGrantPermissions);
             if (result == PERMISSION_DENIED) {
                 throw new SecurityException("User " + uid + " does not have slice permission for "
                         + uri + ".");
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index fb8a83b..e6c88a3 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -452,8 +452,8 @@
         String pkg = callingPkg != null ? callingPkg
                 : getContext().getPackageManager().getNameForUid(callingUid);
         try {
-            mSliceManager.enforceSlicePermission(sliceUri, pkg,
-                    callingPid, callingUid, mAutoGrantPermissions);
+            mSliceManager.enforceSlicePermission(sliceUri, callingPid, callingUid,
+                    mAutoGrantPermissions);
         } catch (SecurityException e) {
             return createPermissionSlice(getContext(), sliceUri, pkg);
         }
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 257530b..75ab115 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -151,7 +151,7 @@
      * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
      * "self-managed" association.
      */
-    private final @Nullable CharSequence mDisplayName;
+    private @Nullable CharSequence mDisplayName;
 
     /**
      * Whether the association is to be managed by the companion application.
@@ -302,6 +302,11 @@
     }
 
     /** @hide */
+    public void setDisplayName(CharSequence displayName) {
+        mDisplayName = displayName;
+    }
+
+    /** @hide */
     @NonNull
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public List<DeviceFilter<?>> getDeviceFilters() {
@@ -597,7 +602,9 @@
         boolean forceConfirmation = (flg & 0x20) != 0;
         boolean skipPrompt = (flg & 0x400) != 0;
         List<DeviceFilter<?>> deviceFilters = new ArrayList<>();
-        in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(), (Class<android.companion.DeviceFilter<?>>) (Class<?>) android.companion.DeviceFilter.class);
+        in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(),
+                (Class<android.companion.DeviceFilter<?>>) (Class<?>)
+                        android.companion.DeviceFilter.class);
         String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
         CharSequence displayName = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence();
         String packageName = (flg & 0x40) == 0 ? null : in.readString();
@@ -641,10 +648,10 @@
     };
 
     @DataClass.Generated(
-            time = 1643238443303L,
+            time = 1649179640045L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
-            inputSignatures = "public static final  java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final  boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate final @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final  boolean mSelfManaged\nprivate final  boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final  long mCreationTime\nprivate  boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic  boolean isSelfManaged()\npublic  boolean isForceConfirmation()\npublic  boolean isSingleDevice()\npublic  void setPackageName(java.lang.String)\npublic  void setUserId(int)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic  void setSkipPrompt(boolean)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate  boolean mSelfManaged\nprivate  boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)")
+            inputSignatures = "public static final  java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final  boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final  boolean mSelfManaged\nprivate final  boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final  long mCreationTime\nprivate  boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic  boolean isSelfManaged()\npublic  boolean isForceConfirmation()\npublic  boolean isSingleDevice()\npublic  void setPackageName(java.lang.String)\npublic  void setUserId(int)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic  void setSkipPrompt(boolean)\npublic  void setDisplayName(java.lang.CharSequence)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate  boolean mSelfManaged\nprivate  boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 56939f0..10ab034 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -774,7 +774,8 @@
     }
 
     /**
-     * Dispatch a message to system for processing.
+     * Dispatch a message to system for processing. It should only be called by
+     * {@link CompanionDeviceService#dispatchMessageToSystem(int, int, byte[])}
      *
      * <p>Calling app must declare uses-permission
      * {@link android.Manifest.permission#DELIVER_COMPANION_MESSAGES}</p>
@@ -874,6 +875,66 @@
         }
     }
 
+    /**
+     * Build a permission sync user consent dialog.
+     *
+     * <p>Only the companion app which owns the association can call this method. Otherwise a null
+     * IntentSender will be returned from this method and an error will be logged.
+     * The The app should launch the {@link Activity} in the returned {@code intentSender}
+     * {@link IntentSender} by calling
+     * {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}.</p>
+     *
+     * <p>The permission transfer doesn't happen immediately after the call or user consented.
+     * The app needs to trigger the system data transfer manually by calling
+     * {@link #startSystemDataTransfer(int)}, when it confirms the communication channel between
+     * the two devices is established.</p>
+     *
+     * @param associationId The unique {@link AssociationInfo#getId ID} assigned to the association
+     *                      of the companion device recorded by CompanionDeviceManager
+     * @return An {@link IntentSender} that the app should use to launch the UI for
+     *         the user to confirm the system data transfer request.
+     */
+    @UserHandleAware
+    @Nullable
+    public IntentSender buildPermissionTransferUserConsentIntent(int associationId)
+            throws DeviceNotAssociatedException {
+        try {
+            PendingIntent pendingIntent = mService.buildPermissionTransferUserConsentIntent(
+                    mContext.getOpPackageName(),
+                    mContext.getUserId(),
+                    associationId);
+            if (pendingIntent == null) {
+                return null;
+            }
+            return pendingIntent.getIntentSender();
+        } catch (RemoteException e) {
+            ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Start system data transfer which has been previously approved by the user.
+     *
+     * <p>Before calling this method, the app needs to make sure there's a communication channel
+     * between two devices, and has prompted user consent dialogs built by one of these methods:
+     * {@link #buildPermissionTransferUserConsentIntent(int)}.
+     * The transfer may fail if the communication channel is disconnected during the transfer.</p>
+     *
+     * @param associationId The unique {@link AssociationInfo#getId ID} assigned to the Association
+     *                      of the companion device recorded by CompanionDeviceManager
+     * @throws DeviceNotAssociatedException Exception if the companion device is not associated
+     */
+    @UserHandleAware
+    public void startSystemDataTransfer(int associationId) throws DeviceNotAssociatedException {
+        try {
+            mService.startSystemDataTransfer(mContext.getUserId(), associationId);
+        } catch (RemoteException e) {
+            ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private boolean checkFeaturePresent() {
         boolean featurePresent = mService != null;
         if (!featurePresent && DEBUG) {
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index 9e1bf4b..bb2189d 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * A service that receives calls from the system when the associated companion device appears
@@ -152,11 +153,9 @@
      * @param messageId system assigned id of the message to be sent
      * @param associationId association id of the associated device
      * @param message message to be sent
-     *
-     * @hide
      */
-    @MainThread
-    public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) {
+    public void onMessageDispatchedFromSystem(int messageId, int associationId,
+            @NonNull byte[] message) {
         // do nothing. Companion apps can override this function for system to send messages.
     }
 
@@ -167,17 +166,31 @@
      * <p>Calling app must declare uses-permission
      * {@link android.Manifest.permission#DELIVER_COMPANION_MESSAGES}</p>
      *
+     * <p>Note 1: messageId was assigned by the system, and sender should send the messageId along
+     * with the message to the receiver. messageId will later be used for verification purpose.
+     * Misusing the messageId will result in no action.</p>
+     *
+     * <p>Note 2: associationId should be local to your device which is calling this API. It's not
+     * the associationId on your remote device. If you don't have one, you can call
+     * {@link CompanionDeviceManager#associate(AssociationRequest, Executor,
+     * CompanionDeviceManager.Callback)} to create one. Misusing the associationId will result in
+     * {@link DeviceNotAssociatedException}.</p>
+     *
      * @param messageId id of the message
      * @param associationId id of the associated device
-     * @param message messaged received from the associated device
-     *
-     * @hide
+     * @param message message received from the associated device
      */
     @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES)
-    public final void dispatchMessage(int messageId, int associationId, @NonNull byte[] message) {
+    public final void dispatchMessageToSystem(int messageId, int associationId,
+            @NonNull byte[] message)
+            throws DeviceNotAssociatedException {
         CompanionDeviceManager companionDeviceManager =
                 getSystemService(CompanionDeviceManager.class);
-        companionDeviceManager.dispatchMessage(messageId, associationId, message);
+        if (companionDeviceManager != null) {
+            companionDeviceManager.dispatchMessage(messageId, associationId, message);
+        } else {
+            Log.e(LOG_TAG, "CompanionDeviceManager is null. Can't dispatch messages.");
+        }
     }
 
     /**
@@ -239,9 +252,11 @@
         }
 
         @Override
-        public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) {
+        public void onMessageDispatchedFromSystem(int messageId, int associationId,
+                @NonNull byte[] message) {
             mMainHandler.postAtFrontOfQueue(
-                    () -> mService.onDispatchMessage(messageId, associationId, message));
+                    () -> mService.onMessageDispatchedFromSystem(messageId, associationId,
+                            message));
         }
     }
 }
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 68a6031f..f5b6938 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -62,7 +62,7 @@
     void createAssociation(in String packageName, in String macAddress, int userId,
         in byte[] certificate);
 
-    void dispatchMessage(in int messageId, in int associationId, in byte[] message);
+    void dispatchMessage(int messageId, int associationId, in byte[] message);
 
     void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
 
@@ -71,4 +71,9 @@
     void notifyDeviceAppeared(int associationId);
 
     void notifyDeviceDisappeared(int associationId);
+
+    PendingIntent buildPermissionTransferUserConsentIntent(String callingPackage, int userId,
+        int associationId);
+
+    void startSystemDataTransfer(int userId, int associationId);
 }
diff --git a/core/java/android/companion/ICompanionDeviceService.aidl b/core/java/android/companion/ICompanionDeviceService.aidl
index 4e453573..3c90b86 100644
--- a/core/java/android/companion/ICompanionDeviceService.aidl
+++ b/core/java/android/companion/ICompanionDeviceService.aidl
@@ -22,5 +22,5 @@
 oneway interface ICompanionDeviceService {
     void onDeviceAppeared(in AssociationInfo associationInfo);
     void onDeviceDisappeared(in AssociationInfo associationInfo);
-    void onDispatchMessage(in int messageId, in int associationId, in byte[] message);
+    void onMessageDispatchedFromSystem(in int messageId, in int associationId, in byte[] message);
 }
diff --git a/core/java/android/companion/SystemDataTransferRequest.java b/core/java/android/companion/SystemDataTransferRequest.java
deleted file mode 100644
index e3b0369..0000000
--- a/core/java/android/companion/SystemDataTransferRequest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.companion;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.provider.OneTimeUseBuilder;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * A request for users to allow the companion app to transfer system data to the companion devices.
- *
- * @hide
- */
-public final class SystemDataTransferRequest implements Parcelable {
-
-    private final int mAssociationId;
-    private final boolean mPermissionSyncAllPackages;
-    private final List<String> mPermissionSyncPackages;
-
-    /**
-     * @hide
-     */
-    public SystemDataTransferRequest(int associationId, boolean syncAllPackages,
-            @Nullable List<String> permissionSyncPackages) {
-        mAssociationId = associationId;
-        mPermissionSyncAllPackages = syncAllPackages;
-        mPermissionSyncPackages = permissionSyncPackages;
-    }
-
-    public int getAssociationId() {
-        return mAssociationId;
-    }
-
-    @NonNull
-    public boolean isPermissionSyncAllPackages() {
-        return mPermissionSyncAllPackages;
-    }
-
-    @NonNull
-    public List<String> getPermissionSyncPackages() {
-        return mPermissionSyncPackages;
-    }
-
-    /**
-     * A builder for {@link SystemDataTransferRequest}.
-     *
-     * <p>You have to call one of the below methods to create a valid request</p>
-     * <br>1. {@link #setPermissionSyncAllPackages()}
-     * <br>2. {@link #setPermissionSyncPackages(List)}
-     */
-    public static final class Builder extends OneTimeUseBuilder<SystemDataTransferRequest> {
-
-        private final int mAssociationId;
-        private boolean mPermissionSyncAllPackages;
-        private List<String> mPermissionSyncPackages = new ArrayList<>();
-
-        public Builder(int associationId) {
-            mAssociationId = associationId;
-        }
-
-        /**
-         * Call to sync permissions for all the packages. You can optionally call
-         * {@link #setPermissionSyncPackages(List)} to specify the packages to sync permissions.
-         *
-         * <p>The system will only sync permissions that are explicitly granted by the user.</p>
-         *
-         * <p>If a permission is granted or revoked by the system or a policy, even if the user has
-         * explicitly granted or revoked the permission earlier, the permission will be ignored.</p>
-         *
-         * <p>If a system or policy granted or revoked permission is granted or revoked by the user
-         * later, the permission will be ignored.</p>
-         *
-         * @see #setPermissionSyncPackages(List)
-         *
-         * @return the builder
-         */
-        @NonNull
-        public Builder setPermissionSyncAllPackages() {
-            mPermissionSyncAllPackages = true;
-            return this;
-        }
-
-        /**
-         * Set a list of packages to sync permissions. You can optionally call
-         * {@link #setPermissionSyncAllPackages()} to sync permissions for all the packages.
-         *
-         * @see #setPermissionSyncAllPackages()
-         *
-         * @param permissionSyncPackages packages to sync permissions
-         * @return builder
-         */
-        @NonNull
-        public Builder setPermissionSyncPackages(@NonNull List<String> permissionSyncPackages) {
-            mPermissionSyncPackages = permissionSyncPackages;
-            return this;
-        }
-
-        @Override
-        @NonNull
-        public SystemDataTransferRequest build() {
-            return new SystemDataTransferRequest(mAssociationId, mPermissionSyncAllPackages,
-                    mPermissionSyncPackages);
-        }
-    }
-
-    SystemDataTransferRequest(Parcel in) {
-        mAssociationId = in.readInt();
-        mPermissionSyncAllPackages = in.readBoolean();
-        mPermissionSyncPackages = Arrays.asList(in.createString8Array());
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mAssociationId);
-        dest.writeBoolean(mPermissionSyncAllPackages);
-        dest.writeString8Array(mPermissionSyncPackages.toArray(new String[0]));
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @NonNull
-    public static final Creator<SystemDataTransferRequest> CREATOR =
-            new Creator<SystemDataTransferRequest>() {
-                @Override
-                public SystemDataTransferRequest createFromParcel(Parcel in) {
-                    return new SystemDataTransferRequest(in);
-                }
-
-                @Override
-                public SystemDataTransferRequest[] newArray(int size) {
-                    return new SystemDataTransferRequest[size];
-                }
-            };
-}
diff --git a/core/java/android/companion/SystemDataTransferRequest.aidl b/core/java/android/companion/datatransfer/PermissionSyncRequest.aidl
similarity index 88%
rename from core/java/android/companion/SystemDataTransferRequest.aidl
rename to core/java/android/companion/datatransfer/PermissionSyncRequest.aidl
index 19ae60e..76a92e3 100644
--- a/core/java/android/companion/SystemDataTransferRequest.aidl
+++ b/core/java/android/companion/datatransfer/PermissionSyncRequest.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.companion;
+package android.companion.datatransfer;
 
-parcelable SystemDataTransferRequest;
+parcelable PermissionSyncRequest;
diff --git a/core/java/android/companion/datatransfer/PermissionSyncRequest.java b/core/java/android/companion/datatransfer/PermissionSyncRequest.java
new file mode 100644
index 0000000..973fca30
--- /dev/null
+++ b/core/java/android/companion/datatransfer/PermissionSyncRequest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.datatransfer;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The permission sync request class.
+ *
+ * @hide
+ */
+public class PermissionSyncRequest extends SystemDataTransferRequest implements Parcelable {
+
+    /** @hide */
+    public PermissionSyncRequest(int associationId) {
+        super(associationId, DATA_TYPE_PERMISSION_SYNC);
+    }
+
+    /** @hide */
+    @Override
+    public String toString() {
+        return "SystemDataTransferRequest("
+                + "associationId=" + mAssociationId
+                + ", userId=" + mUserId
+                + ", isUserConsented=" + mUserConsented
+                + ")";
+    }
+
+    /** @hide */
+    PermissionSyncRequest(Parcel in) {
+        super(in);
+    }
+
+    /** @hide */
+    @NonNull
+    public static final Creator<PermissionSyncRequest> CREATOR =
+            new Creator<PermissionSyncRequest>() {
+                @Override
+                public PermissionSyncRequest createFromParcel(Parcel in) {
+                    return new PermissionSyncRequest(in);
+                }
+
+                @Override
+                public PermissionSyncRequest[] newArray(int size) {
+                    return new PermissionSyncRequest[size];
+                }
+            };
+}
diff --git a/core/java/android/companion/datatransfer/SystemDataTransferRequest.java b/core/java/android/companion/datatransfer/SystemDataTransferRequest.java
new file mode 100644
index 0000000..38a553d
--- /dev/null
+++ b/core/java/android/companion/datatransfer/SystemDataTransferRequest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.datatransfer;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.Parcel;
+
+/**
+ * A request for users to allow the companion app to transfer system data to the companion devices.
+ *
+ * @hide
+ */
+public abstract class SystemDataTransferRequest {
+
+    /** @hide */
+    public static final int DATA_TYPE_PERMISSION_SYNC = 1;
+
+    final int mAssociationId;
+
+    final int mDataType;
+
+    /**
+     * User id that the request belongs to.
+     * Populated by the system.
+     */
+    @UserIdInt
+    int mUserId;
+
+    /**
+     * Whether the request is consented by the user.
+     * Populated by the system
+     */
+    boolean mUserConsented = false;
+
+    /** @hide */
+    SystemDataTransferRequest(int associationId, int dataType) {
+        mAssociationId = associationId;
+        mDataType = dataType;
+    }
+
+    /** @hide */
+    public int getAssociationId() {
+        return mAssociationId;
+    }
+
+    /** @hide */
+    public int getDataType() {
+        return mDataType;
+    }
+
+    /** @hide */
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /** @hide */
+    public boolean isUserConsented() {
+        return mUserConsented;
+    }
+
+    /** @hide */
+    public void setUserId(@UserIdInt int userId) {
+        mUserId = userId;
+    }
+
+    /** @hide */
+    public void setUserConsented(boolean isUserConsented) {
+        mUserConsented = isUserConsented;
+    }
+
+    /** @hide */
+    SystemDataTransferRequest(Parcel in) {
+        mAssociationId = in.readInt();
+        mDataType = in.readInt();
+        mUserId = in.readInt();
+        mUserConsented = in.readBoolean();
+    }
+
+    /** @hide */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mAssociationId);
+        dest.writeInt(mDataType);
+        dest.writeInt(mUserId);
+        dest.writeBoolean(mUserConsented);
+    }
+
+    /** @hide */
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index f4de829..9708493 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -448,6 +448,12 @@
      */
     public boolean isApex;
 
+    /**
+     * Whether this is an active APEX package.
+     * @hide
+     */
+    public boolean isActiveApex;
+
     public PackageInfo() {
     }
 
@@ -534,6 +540,7 @@
             dest.writeInt(0);
         }
         dest.writeBoolean(isApex);
+        dest.writeBoolean(isActiveApex);
         dest.restoreAllowSquashing(prevAllowSquashing);
     }
 
@@ -598,5 +605,6 @@
             signingInfo = SigningInfo.CREATOR.createFromParcel(source);
         }
         isApex = source.readBoolean();
+        isActiveApex = source.readBoolean();
     }
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 236c244..5f45c342 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -682,7 +682,9 @@
      * </ul>
      *
      * @param packageName The package to uninstall.
-     * @param statusReceiver Where to deliver the result.
+     * @param statusReceiver Where to deliver the result of the operation indicated by the extra
+     *                       {@link #EXTRA_STATUS}. Refer to the individual status codes
+     *                       on how to handle them.
      *
      * @see android.app.admin.DevicePolicyManager
      */
@@ -700,7 +702,9 @@
      *
      * @param packageName The package to uninstall.
      * @param flags Flags for uninstall.
-     * @param statusReceiver Where to deliver the result.
+     * @param statusReceiver Where to deliver the result of the operation indicated by the extra
+     *                       {@link #EXTRA_STATUS}. Refer to the individual status codes
+     *                       on how to handle them.
      *
      * @hide
      */
@@ -725,7 +729,9 @@
      * </ul>
      *
      * @param versionedPackage The versioned package to uninstall.
-     * @param statusReceiver Where to deliver the result.
+     * @param statusReceiver Where to deliver the result of the operation indicated by the extra
+     *                       {@link #EXTRA_STATUS}. Refer to the individual status codes
+     *                       on how to handle them.
      *
      * @see android.app.admin.DevicePolicyManager
      */
@@ -747,7 +753,9 @@
      *
      * @param versionedPackage The versioned package to uninstall.
      * @param flags Flags for uninstall.
-     * @param statusReceiver Where to deliver the result.
+     * @param statusReceiver Where to deliver the result of the operation indicated by the extra
+     *                       {@link #EXTRA_STATUS}. Refer to the individual status codes
+     *                       on how to handle them.
      *
      * @hide
      */
@@ -775,7 +783,9 @@
      *
      * @param packageName The package to install.
      * @param installReason Reason for install.
-     * @param statusReceiver Where to deliver the result.
+     * @param statusReceiver Where to deliver the result of the operation indicated by the extra
+     *                       {@link #EXTRA_STATUS}. Refer to the individual status codes
+     *                       on how to handle them.
      */
     @RequiresPermission(allOf = {
             Manifest.permission.INSTALL_PACKAGES,
@@ -797,8 +807,10 @@
      * Uninstall the given package for the user for which this installer was created if the package
      * will still exist for other users on the device.
      *
-     * @param packageName The package to install.
-     * @param statusReceiver Where to deliver the result.
+     * @param packageName The package to uninstall.
+     * @param statusReceiver Where to deliver the result of the operation indicated by the extra
+     *                       {@link #EXTRA_STATUS}. Refer to the individual status codes
+     *                       on how to handle them.
      */
     @RequiresPermission(Manifest.permission.DELETE_PACKAGES)
     public void uninstallExistingPackage(@NonNull String packageName,
@@ -1705,20 +1717,22 @@
         public @interface UserActionRequirement {}
 
         /**
-         * The installer did not call {@link SessionParams#setRequireUserAction(int)} to
-         * specify whether user action should be required for the install.
+         * This value is passed by the installer to {@link SessionParams#setRequireUserAction(int)}
+         * to indicate that user action is unspecified for this install.
+         * {@code requireUserAction} also defaults to this value unless modified by
+         * {@link SessionParams#setRequireUserAction(int)}
          */
         public static final int USER_ACTION_UNSPECIFIED = 0;
 
         /**
-         * The installer called {@link SessionParams#setRequireUserAction(int)} with
-         * {@code true} to require user action for the install to complete.
+         * This value is passed by the installer to {@link SessionParams#setRequireUserAction(int)}
+         * to indicate that user action is required for this install.
          */
         public static final int USER_ACTION_REQUIRED = 1;
 
         /**
-         * The installer called {@link SessionParams#setRequireUserAction(int)} with
-         * {@code false} to request that user action not be required for this install.
+         * This value is passed by the installer to {@link SessionParams#setRequireUserAction(int)}
+         * to indicate that user action is not required for this install.
          */
         public static final int USER_ACTION_NOT_REQUIRED = 2;
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a90f6d6..6f9ec9c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4247,6 +4247,7 @@
      */
     public static final String EXTRA_VERIFICATION_PACKAGE_NAME
             = "android.content.pm.extra.VERIFICATION_PACKAGE_NAME";
+
     /**
      * Extra field name for the result of a verification, either
      * {@link #VERIFICATION_ALLOW}, or {@link #VERIFICATION_REJECT}.
@@ -4256,6 +4257,14 @@
             = "android.content.pm.extra.VERIFICATION_RESULT";
 
     /**
+     * Extra field name for tracking whether user action
+     * was requested for a particular install, either {@code true} or {@code false}.
+     * @hide
+     */
+    public static final String EXTRA_USER_ACTION_REQUIRED
+            = "android.content.pm.extra.USER_ACTION_REQUIRED";
+
+    /**
      * Extra field name for the version code of a package pending verification.
      * @deprecated Use {@link #EXTRA_VERIFICATION_LONG_VERSION_CODE} instead.
      * @hide
@@ -4265,8 +4274,7 @@
             = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
 
     /**
-     * Extra field name for the long version code of a package pending verification.
-     *
+     * Extra field name for the long version code of a package pending verification
      * @hide
      */
     public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE =
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 76e9fcb..816460b 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -142,6 +142,22 @@
     public static final int FLAG_PROFILE = 0x00001000;
 
     /**
+     * Indicates that this user is created in ephemeral mode via
+     * {@link IUserManager} create user.
+     *
+     * When a user is created with {@link #FLAG_EPHEMERAL}, {@link #FLAG_EPHEMERAL_ON_CREATE}
+     * is set internally within the user manager.
+     *
+     * When {@link #FLAG_EPHEMERAL_ON_CREATE} is set {@link IUserManager.setUserEphemeral}
+     * has no effect because a user that was created ephemeral can never be made non-ephemeral.
+     *
+     * {@link #FLAG_EPHEMERAL_ON_CREATE} should NOT be set by client's of user manager
+     *
+     * @hide
+     */
+    public static final int FLAG_EPHEMERAL_ON_CREATE = 0x00002000;
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = "FLAG_", value = {
@@ -157,7 +173,8 @@
             FLAG_DEMO,
             FLAG_FULL,
             FLAG_SYSTEM,
-            FLAG_PROFILE
+            FLAG_PROFILE,
+            FLAG_EPHEMERAL_ON_CREATE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserInfoFlag {
diff --git a/core/java/android/hardware/IConsumerIrService.aidl b/core/java/android/hardware/IConsumerIrService.aidl
index c79bd19..930c73f 100644
--- a/core/java/android/hardware/IConsumerIrService.aidl
+++ b/core/java/android/hardware/IConsumerIrService.aidl
@@ -19,8 +19,13 @@
 /** {@hide} */
 interface IConsumerIrService
 {
+    @RequiresNoPermission
     boolean hasIrEmitter();
+
+    @EnforcePermission("TRANSMIT_IR")
     void transmit(String packageName, int carrierFrequency, in int[] pattern);
+
+    @EnforcePermission("TRANSMIT_IR")
     int[] getCarrierFrequencies();
 }
 
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a62bbf6..36b532f 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -41,6 +41,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.Trace;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -987,7 +988,8 @@
         @Override
         public void onDisplayEvent(int displayId, @DisplayEvent int event) {
             if (DEBUG) {
-                Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
+                Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + eventToString(
+                        event));
             }
             handleDisplayEvent(displayId, event);
         }
@@ -1021,6 +1023,12 @@
 
         @Override
         public void handleMessage(Message msg) {
+            if (DEBUG) {
+                Trace.beginSection(
+                        "DisplayListenerDelegate(" + eventToString(msg.what)
+                                + ", display=" + msg.arg1
+                                + ", listener=" + mListener.getClass() + ")");
+            }
             switch (msg.what) {
                 case EVENT_DISPLAY_ADDED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
@@ -1047,6 +1055,9 @@
                     }
                     break;
             }
+            if (DEBUG) {
+                Trace.endSection();
+            }
         }
     }
 
@@ -1150,4 +1161,18 @@
             updateCallbackIfNeededLocked();
         }
     }
+
+    private static String eventToString(@DisplayEvent int event) {
+        switch (event) {
+            case EVENT_DISPLAY_ADDED:
+                return "ADDED";
+            case EVENT_DISPLAY_CHANGED:
+                return "CHANGED";
+            case EVENT_DISPLAY_REMOVED:
+                return "REMOVED";
+            case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
+                return "BRIGHTNESS_CHANGED";
+        }
+        return "UNKNOWN";
+    }
 }
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index e1ffd4a..bb8af8f 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -38,6 +38,8 @@
 
 /** @hide */
 interface IInputManager {
+    // Gets the current VelocityTracker strategy
+    String getVelocityTrackerStrategy();
     // Gets input device information.
     InputDevice getInputDevice(int deviceId);
     int[] getInputDeviceIds();
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index cc5b275..f59b9f7 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -272,8 +272,15 @@
      */
     public static final int SWITCH_STATE_ON = 1;
 
+    private static String sVelocityTrackerStrategy;
+
     private InputManager(IInputManager im) {
         mIm = im;
+        try {
+            sVelocityTrackerStrategy = mIm.getVelocityTrackerStrategy();
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not get VelocityTracker strategy: " + ex);
+        }
     }
 
     /**
@@ -326,10 +333,19 @@
     }
 
     /**
+     * Get the current VelocityTracker strategy. Only works when the system has fully booted up.
+     * @hide
+     */
+    public String getVelocityTrackerStrategy() {
+        return sVelocityTrackerStrategy;
+    }
+
+    /**
      * Gets information about the input device with the specified id.
      * @param id The device id.
      * @return The input device or null if not found.
      */
+    @Nullable
     public InputDevice getInputDevice(int id) {
         synchronized (mInputDevicesLock) {
             populateInputDevicesLocked();
@@ -1377,6 +1393,7 @@
      * </p>
      * @hide
      */
+    @TestApi
     public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
         try {
             mIm.addUniqueIdAssociation(inputPort, displayUniqueId);
@@ -1393,6 +1410,7 @@
      * </p>
      * @hide
      */
+    @TestApi
     public void removeUniqueIdAssociation(@NonNull String inputPort) {
         try {
             mIm.removeUniqueIdAssociation(inputPort);
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index 3a6d508..694d6d8c 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -40,11 +40,11 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -151,7 +151,7 @@
             return;
         }
 
-        List<String> parseErrors = new LinkedList<>();
+        List<String> parseErrors = new ArrayList<>();
         mKeyphrasePackageMap = new HashMap<>();
         for (ResolveInfo ri : ris) {
             try {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4fdd534..378b366 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -143,6 +143,7 @@
 import com.android.internal.inputmethod.InputMethodNavButtonFlags;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.util.RingBuffer;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.IInputContext;
@@ -1059,7 +1060,7 @@
     }
 
     private void notifyImeHidden() {
-        requestHideSelf(0);
+        requestHideSelf(0 /* flags */, SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
     }
 
     private void scheduleImeSurfaceRemoval() {
@@ -2907,9 +2908,13 @@
      * @param flags Provides additional operating flags.
      */
     public void requestHideSelf(int flags) {
+        requestHideSelf(flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME);
+    }
+
+    private void requestHideSelf(int flags, @SoftInputShowHideReason int reason) {
         ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
                 null /* icProto */);
-        mPrivOps.hideMySoftInput(flags);
+        mPrivOps.hideMySoftInput(flags, reason);
     }
 
     /**
@@ -2930,7 +2935,9 @@
         if (mShowInputRequested) {
             // If the soft input area is shown, back closes it and we
             // consume the back key.
-            if (doIt) requestHideSelf(0);
+            if (doIt) {
+                requestHideSelf(0 /* flags */, SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY);
+            }
             return true;
         } else if (mDecorViewVisible) {
             if (mCandidatesVisibility == View.VISIBLE) {
@@ -3081,7 +3088,8 @@
     private void onToggleSoftInput(int showFlags, int hideFlags) {
         if (DEBUG) Log.v(TAG, "toggleSoftInput()");
         if (isInputViewShown()) {
-            requestHideSelf(hideFlags);
+            requestHideSelf(
+                    hideFlags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT);
         } else {
             requestShowSelf(showFlags);
         }
@@ -3516,7 +3524,8 @@
      */
     public void onExtractingInputChanged(EditorInfo ei) {
         if (ei.inputType == InputType.TYPE_NULL) {
-            requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
+            requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS,
+                    SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED);
         }
     }
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2c2a703..9341105 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -817,11 +817,13 @@
     public static final class UidState {
         public int uid;
         public int procState;
+        public long procStateSeq;
         public int capability;
 
-        public UidState(int uid, int procState, int capability) {
+        public UidState(int uid, int procState, long procStateSeq, int capability) {
             this.uid = uid;
             this.procState = procState;
+            this.procStateSeq = procStateSeq;
             this.capability = capability;
         }
 
@@ -830,6 +832,8 @@
             final StringBuilder sb = new StringBuilder();
             sb.append("{procState=");
             sb.append(procStateToString(procState));
+            sb.append(",seq=");
+            sb.append(procStateSeq);
             sb.append(",cap=");
             ActivityManager.printCapabilitiesSummary(sb, capability);
             sb.append("}");
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 3cde031..e5de3e1 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -131,4 +131,5 @@
     String getUserName();
     long getUserStartRealtime();
     long getUserUnlockRealtime();
+    boolean setUserEphemeral(int userId, boolean enableEphemeral);
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e06e732..14082f3 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -213,12 +213,6 @@
     public static final int SE_UID = 1068;
 
     /**
-     * Defines the UID/GID for the iorapd.
-     * @hide
-     */
-    public static final int IORAPD_UID = 1071;
-
-    /**
      * Defines the UID/GID for the NetworkStack app.
      * @hide
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index a64e63e..570d533 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1997,13 +1997,22 @@
      * @return Whether guest user is always ephemeral
      * @hide
      */
-    @TestApi
-    public static boolean isGuestUserEphemeral() {
+    public static boolean isGuestUserAlwaysEphemeral() {
         return Resources.getSystem()
                 .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
     }
 
     /**
+     * @return true, when we want to enable user manager API and UX to allow
+     *           guest user ephemeral state change based on user input
+     * @hide
+     */
+    public static boolean isGuestUserAllowEphemeralStateChange() {
+        return Resources.getSystem()
+                .getBoolean(com.android.internal.R.bool.config_guestUserAllowEphemeralStateChange);
+    }
+
+    /**
      * Checks whether the device is running in a headless system user mode.
      *
      * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system
@@ -3424,6 +3433,20 @@
             if (guest != null) {
                 Settings.Secure.putStringForUser(context.getContentResolver(),
                         Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
+
+                if (UserManager.isGuestUserAllowEphemeralStateChange()) {
+                    // Mark guest as (changeably) ephemeral if REMOVE_GUEST_ON_EXIT is 1
+                    // This is done so that a user via a UI controller can choose to
+                    // make a guest as ephemeral or not.
+                    // Settings.Global.REMOVE_GUEST_ON_EXIT holds the choice on what the guest state
+                    // should be, with default being ephemeral.
+                    boolean resetGuestOnExit = Settings.Global.getInt(context.getContentResolver(),
+                                                 Settings.Global.REMOVE_GUEST_ON_EXIT, 1) == 1;
+
+                    if (resetGuestOnExit && !guest.isEphemeral()) {
+                        setUserEphemeral(guest.id, true);
+                    }
+                }
             }
             return guest;
         } catch (ServiceSpecificException e) {
@@ -4941,6 +4964,31 @@
     }
 
     /**
+     * Set the user as ephemeral or non-ephemeral.
+     *
+     * If the user was initially created as ephemeral then this
+     * method has no effect and false is returned.
+     *
+     * @param userId the user's integer id
+     * @param enableEphemeral true: change user state to ephemeral,
+     *                        false: change user state to non-ephemeral
+     * @return true: user now has the desired ephemeral state,
+     *         false: desired user ephemeral state could not be set
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS})
+    public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) {
+        try {
+            return mService.setUserEphemeral(userId, enableEphemeral);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Updates the context user's name.
      *
      * @param name the new name for the user
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index ec1c57d..237f6ed 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -413,10 +413,8 @@
      * {@link #startWaveform(VibrationEffect.VibrationParameter)}.
      *
      * @see VibrationEffect.WaveformBuilder
-     * @hide
      */
     @NonNull
-    @TestApi
     public static WaveformBuilder startWaveform() {
         return new WaveformBuilder();
     }
@@ -433,10 +431,8 @@
      * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
      *
      * @see VibrationEffect.WaveformBuilder
-     * @hide
      */
     @NonNull
-    @TestApi
     public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter) {
         WaveformBuilder builder = startWaveform();
         builder.addTransition(Duration.ZERO, initialParameter);
@@ -458,10 +454,8 @@
      * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
      *
      * @see VibrationEffect.WaveformBuilder
-     * @hide
      */
     @NonNull
-    @TestApi
     public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter1,
             @NonNull VibrationParameter initialParameter2) {
         WaveformBuilder builder = startWaveform();
@@ -875,9 +869,7 @@
         /**
          * Exception thrown when adding an element to a {@link Composition} that already ends in an
          * indefinitely repeating effect.
-         * @hide
          */
-        @TestApi
         public static final class UnreachableAfterRepeatingIndefinitelyException
                 extends IllegalStateException {
             UnreachableAfterRepeatingIndefinitelyException() {
@@ -946,10 +938,8 @@
          *
          * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
          * ending with a repeating effect.
-         * @hide
          */
         @NonNull
-        @TestApi
         public Composition addOffDuration(@NonNull Duration duration) {
             int durationMs = (int) duration.toMillis();
             Preconditions.checkArgumentNonnegative(durationMs, "Off period must be non-negative");
@@ -975,10 +965,8 @@
          *
          * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
          * ending with a repeating effect.
-         * @hide
          */
         @NonNull
-        @TestApi
         public Composition addEffect(@NonNull VibrationEffect effect) {
             return addSegments(effect);
         }
@@ -999,10 +987,8 @@
          * @throws IllegalArgumentException if the given effect is already repeating indefinitely.
          * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
          * ending with a repeating effect.
-         * @hide
          */
         @NonNull
-        @TestApi
         public Composition repeatEffectIndefinitely(@NonNull VibrationEffect effect) {
             Preconditions.checkArgument(effect.getDuration() < Long.MAX_VALUE,
                     "Can't repeat an indefinitely repeating effect. Consider addEffect instead.");
@@ -1216,9 +1202,7 @@
      *     .build();}</pre>
      *
      * @see VibrationEffect#startWaveform
-     * @hide
      */
-    @TestApi
     public static final class WaveformBuilder {
         // Epsilon used for float comparison of amplitude and frequency values on transitions.
         private static final float EPSILON = 1e-5f;
@@ -1399,10 +1383,8 @@
      * <p>Examples of concrete parameters are the vibration amplitude or frequency.
      *
      * @see VibrationEffect.WaveformBuilder
-     * @hide
      */
     @SuppressWarnings("UserHandleName") // This is not a regular set of parameters, no *Params.
-    @TestApi
     public static class VibrationParameter {
         VibrationParameter() {
         }
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 465d90d..7f0d634 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -212,9 +212,7 @@
      *
      * @return True if the hardware can control the frequency of the vibrations independently of
      * the vibration amplitude, false otherwise.
-     * @hide
      */
-    @TestApi
     public boolean hasFrequencyControl() {
         // We currently can only control frequency of the vibration using the compose PWLE method.
         return getInfo().hasCapability(
@@ -238,9 +236,7 @@
      * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
      * applicable, or if this vibrator is a composite of multiple physical devices with different
      * frequencies.
-     * @hide
      */
-    @TestApi
     public float getResonantFrequency() {
         return getInfo().getResonantFrequencyHz();
     }
@@ -251,9 +247,7 @@
      * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
      * applicable, or if this vibrator is a composite of multiple physical devices with different
      * Q factors.
-     * @hide
      */
-    @TestApi
     public float getQFactor() {
         return getInfo().getQFactor();
     }
@@ -268,9 +262,7 @@
      * frequency control. If this vibrator is a composite of multiple physical devices then this
      * will return a profile supported in all devices, or null if the intersection is empty or not
      * available.
-     * @hide
      */
-    @TestApi
     @Nullable
     public VibratorFrequencyProfile getFrequencyProfile() {
         VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile();
diff --git a/core/java/android/os/vibrator/VibratorFrequencyProfile.java b/core/java/android/os/vibrator/VibratorFrequencyProfile.java
index afc0007..0f2aa15 100644
--- a/core/java/android/os/vibrator/VibratorFrequencyProfile.java
+++ b/core/java/android/os/vibrator/VibratorFrequencyProfile.java
@@ -18,7 +18,6 @@
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.os.VibratorInfo;
 
 import com.android.internal.util.Preconditions;
@@ -39,9 +38,7 @@
  * frequency increment between each pair of amplitude values.
  *
  * <p>Vibrators without independent frequency control do not have a frequency profile.
- * @hide
  */
-@TestApi
 public final class VibratorFrequencyProfile {
 
     private final VibratorInfo.FrequencyProfile mFrequencyProfile;
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index dfe748b..e61ecd8 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -71,9 +71,9 @@
 
 A requested permission does not necessarily mean that the permission is granted. When and how a
 permission is granted depends on the protection level of the permission. If no protection level is
-set, the permission will always be granted. Such "normal" permissions can still be useful as it
-will be easy to find apps using a certain functionality on app stores and by checking `dumpsys
-package`.
+set, it will default to `normal` and the permission will always be granted if requested. Such
+`normal` permissions can still be useful as it will be easy to find apps using a certain
+functionality on app stores and by checking `dumpsys package`.
 
 #### Checking a permission
 
@@ -686,17 +686,37 @@
 these system apps and then enforcing the permissions in the API similar to other [install time
 permissions](#checking-a-permission).
 
-System apps are not different from regular apps, but the protection levels (e.g.
+System apps are not different from regular apps, but the protection flags (e.g.
 [privileged](#privileged-permissions), [preinstalled](#preinstalled-permissions)) mentioned in this
 section are more commonly used by system apps.
 
-### Multiple permission levels
+### Permission protection level
 
-It is possible to assign multiple protection levels to a permission. Very common combinations are
-for example adding `signature` to all permissions to make sure the platform signed apps can be
-granted the permission, e.g. `privileged|signature`.
+Every permission has a protection level (`android:protectionlevel`), which is a combination of one
+required protection (`PermissionInfo.getProtection()`) and multiple optional protection flags
+(`PermissionInfo.getProtectionFlags()`).
 
-The permission will be granted if the app qualifies for _any_ of the permission levels.
+The protection can be one of the following:
+
+- [`normal`](#requesting-a-permission): The permission will be granted to apps requesting it in
+their manifest.
+- [`dangerous`](#runtime-permissions): The permission will be a runtime permission.
+- [`signature`](#signature-permissions): The permission will be granted to apps being signed with
+the same certificate as the app defining the permission. If the permission is a platform permission,
+it means those apps need to be platform-signed.
+- `internal`: This is a no-op protection so that it won't allow granting the permission by itself.
+However, it will be useful when defining permissions that should only be granted according to its
+protection flags, e.g. `internal|role` for a role-only permission.
+
+There are various optional protection flags that can be added to protection level, in addition to
+the required protection, e.g. [appop](#app_op-permissions),
+[preinstalled](#preinstalled-permissions), [privileged](#privileged-permissions),
+[installer](#limited-permissions), [role](#role-protected-permissions) and
+[development](#development-permissions).
+
+The permission will be granted to an app if it meets _any_ of the protection or protection flags (an
+`OR` relationship). For example, `signature|privileged` allows the permission to be granted to
+platform-signed apps as well as privileged apps.
 
 ### App-op permissions
 
@@ -716,18 +736,15 @@
 #### Defining an app-op permission
 
 Only the platform can reasonably define an app-op permission. The permission is defined in the
-platforms manifest using the `appop` protection level
+platforms manifest using the `appop` protection flag:
 
 ```xml
 <manifest package="android">
     <permission android:name="android.permission.MY_APPOP_PERMISSION"
-        android:protectionLevel="appop|signature" />
+        android:protectionLevel="signature|appop" />
 </manifest>
 ```
 
-Almost always the protection level is app-op | something else, like
-[signature](#signature-permissions) (in the case above) or [privileged](#privileged-permissions).
-
 #### Checking an app-op permission
 
 The `PermissionChecker` utility can check app-op permissions with the [same syntax as runtime
@@ -913,12 +930,12 @@
 
 > Not recommended
 
-By adding the `development` protection level to any permissions the permission can be granted via
+By adding the `development` protection flag to any permissions the permission can be granted via
 the `pm grant` shell command. This appears to be useful for development and testing, but it is very
 highly discouraged. Any user can grant them permanently via adb, hence adding this tag removes
 all guarantees the permission might otherwise provide.
 
-### Other protection levels
+### Other protection flags
 
 There are other levels (such as `runtime`) but they are for special purposes on should not be
 used by platform developers.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d8d9c7f..a3227b7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4519,13 +4519,6 @@
         public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
 
         /**
-         * The amount of time in milliseconds before the device goes to sleep or begins to dream
-         * after a period of inactivity while it is docked.
-         * @hide
-         */
-        public static final String SCREEN_OFF_TIMEOUT_DOCKED = "screen_off_timeout_docked";
-
-        /**
          * The screen backlight brightness between 0 and 255.
          */
         @Readable
@@ -10905,6 +10898,14 @@
         public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked";
 
         /**
+         * Whether guest user should be removed on exit from guest mode.
+         * <p>
+         * Type: int
+         * @hide
+         */
+        public static final String REMOVE_GUEST_ON_EXIT = "remove_guest_on_exit";
+
+        /**
          * Whether applying ramping ringer on incoming phone call ringtone.
          * <p>1 = apply ramping ringer
          * <p>0 = do not apply ramping ringer
@@ -17251,6 +17252,12 @@
             public static final String AMBIENT_TILT_TO_BRIGHT = "ambient_tilt_to_bright";
 
             /**
+             * Whether touch and hold to edit WF is enabled
+             * @hide
+             */
+            public static final String TOUCH_AND_HOLD_WATCH_FACE = "touch_and_hold_watchface";
+
+            /**
              * Whether the current watchface is decomposable.
              * @hide
              */
@@ -17468,6 +17475,18 @@
              * @hide
              */
             public static final String WET_MODE_ON = "wet_mode_on";
+
+            /*
+             * Whether the screen-unlock (keyguard) sound is enabled.
+             * @hide
+             */
+            public static final String SCREEN_UNLOCK_SOUND_ENABLED = "screen_unlock_sound_enabled";
+
+            /*
+             * Whether charging sounds are enabled.
+             * @hide
+             */
+            public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
         }
     }
 
diff --git a/core/java/android/security/attestationverification/AttestationVerificationManager.java b/core/java/android/security/attestationverification/AttestationVerificationManager.java
index db783ce..2e61db1 100644
--- a/core/java/android/security/attestationverification/AttestationVerificationManager.java
+++ b/core/java/android/security/attestationverification/AttestationVerificationManager.java
@@ -226,10 +226,10 @@
     public static final int PROFILE_SELF_TRUSTED = 2;
 
     /**
-     * A system-defined profile which verifies that the attesting environment environment is similar
-     * to the current device in terms of security model and security configuration. This category is
-     * fairly broad and most securely configured Android devices should qualify, along with a
-     * variety of non-Android devices.
+     * A system-defined profile which verifies that the attesting environment is similar to the
+     * current device in terms of security model and security configuration. This category is fairly
+     * broad and most securely configured Android devices should qualify, along with a variety of
+     * non-Android devices.
      */
     public static final int PROFILE_PEER_DEVICE = 3;
 
@@ -321,4 +321,52 @@
 
     /** Requirements bundle parameter for a challenge. */
     public static final String PARAM_CHALLENGE = "localbinding.challenge";
+
+    /** @hide */
+    public static String localBindingTypeToString(@LocalBindingType int localBindingType) {
+        final String text;
+        switch (localBindingType) {
+            case TYPE_UNKNOWN:
+                text = "UNKNOWN";
+                break;
+
+            case TYPE_APP_DEFINED:
+                text = "APP_DEFINED";
+                break;
+
+            case TYPE_PUBLIC_KEY:
+                text = "PUBLIC_KEY";
+                break;
+
+            case TYPE_CHALLENGE:
+                text = "CHALLENGE";
+                break;
+
+            default:
+                return Integer.toString(localBindingType);
+        }
+        return text + "(" + localBindingType + ")";
+    }
+
+    /** @hide */
+    public static String verificationResultCodeToString(@VerificationResult int resultCode) {
+        final String text;
+        switch (resultCode) {
+            case RESULT_UNKNOWN:
+                text = "UNKNOWN";
+                break;
+
+            case RESULT_SUCCESS:
+                text = "SUCCESS";
+                break;
+
+            case RESULT_FAILURE:
+                text = "FAILURE";
+                break;
+
+            default:
+                return Integer.toString(resultCode);
+        }
+        return text + "(" + resultCode + ")";
+    }
 }
diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java
index 8331550..cc1b6cd 100644
--- a/core/java/android/service/autofill/FillContext.java
+++ b/core/java/android/service/autofill/FillContext.java
@@ -32,7 +32,7 @@
 
 import com.android.internal.util.DataClass;
 
-import java.util.LinkedList;
+import java.util.ArrayDeque;
 
 /**
  * This class represents a context for each fill request made via {@link
@@ -95,7 +95,7 @@
      * @hide
      */
     @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) {
-        final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
+        final ArrayDeque<ViewNode> nodesToProcess = new ArrayDeque<>();
         final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length];
 
         // Indexes of foundNodes that are not found yet
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 1507c87..327cda3 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -97,7 +97,7 @@
      */
     public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
 
-    // The flag value 0x20 has been used.
+    // The flag value 0x20 has been defined in AutofillManager.
 
     /**
      * Indicates the request supports fill dialog presentation for the fields, the
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 0d290ee..ce38bb8 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -135,7 +135,7 @@
         return null;
     }
 
-    private static class ServiceMetadata {
+    static class ServiceMetadata {
         @Nullable
         private final String mSettingsActivity;
         @Nullable
@@ -161,7 +161,7 @@
         }
     }
 
-    private static ServiceMetadata parseServiceMetadata(Context context, ServiceInfo serviceInfo) {
+    static ServiceMetadata parseServiceMetadata(Context context, ServiceInfo serviceInfo) {
         PackageManager pm = context.getPackageManager();
         final XmlResourceParser parser =
                 serviceInfo.loadXmlMetaData(pm, QuickAccessWalletService.SERVICE_META_DATA);
diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java
index 01d5638..5b3b78b 100644
--- a/core/java/android/service/voice/AbstractHotwordDetector.java
+++ b/core/java/android/service/voice/AbstractHotwordDetector.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
+import android.app.compat.CompatChanges;
 import android.media.AudioFormat;
 import android.media.permission.Identity;
 import android.os.Handler;
@@ -65,6 +66,13 @@
     }
 
     /**
+     * Method to be called for the detector to ready/register itself with underlying system
+     * services.
+     */
+    abstract void initialize(@Nullable PersistableBundle options,
+            @Nullable SharedMemory sharedMemory);
+
+    /**
      * Detect hotword from an externally supplied stream of data.
      *
      * @return true if the request to start recognition succeeded
@@ -73,7 +81,7 @@
     public boolean startRecognition(
             @NonNull ParcelFileDescriptor audioStream,
             @NonNull AudioFormat audioFormat,
-            @Nullable PersistableBundle options) {
+            @Nullable PersistableBundle options) throws IllegalDetectorStateException {
         if (DEBUG) {
             Slog.i(TAG, "#recognizeHotword");
         }
@@ -98,19 +106,22 @@
      * Set configuration and pass read-only data to hotword detection service.
      *
      * @param options Application configuration data to provide to the
-     * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
-     * other contents that can be used to communicate with other processes.
+     *         {@link HotwordDetectionService}. PersistableBundle does not allow any remotable
+     *         objects or other contents that can be used to communicate with other processes.
      * @param sharedMemory The unrestricted data blob to provide to the
-     * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
-     * such data to the trusted process.
-     *
-     * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a
-     * {@link HotwordDetectionService} when it was created. In addition, if this
-     * AlwaysOnHotwordDetector is in an invalid or error state.
+     *         {@link HotwordDetectionService}. Use this to provide the hotword models data or other
+     *         such data to the trusted process.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of
+     *         Android Tiramisu or above and attempts to start a recognition when the detector is
+     *         not able based on the state. Because the caller receives updates via an asynchronous
+     *         callback and the state of the detector can change without caller's knowledge, a
+     *         checked exception is thrown.
+     * @throws IllegalStateException if this HotwordDetector wasn't specified to use a
+     *         {@link HotwordDetectionService} when it was created.
      */
     @Override
     public void updateState(@Nullable PersistableBundle options,
-            @Nullable SharedMemory sharedMemory) {
+            @Nullable SharedMemory sharedMemory) throws IllegalDetectorStateException {
         if (DEBUG) {
             Slog.d(TAG, "updateState()");
         }
@@ -156,9 +167,13 @@
         }
     }
 
-    protected void throwIfDetectorIsNoLongerActive() {
+    protected void throwIfDetectorIsNoLongerActive() throws IllegalDetectorStateException {
         if (!mIsDetectorActive.get()) {
             Slog.e(TAG, "attempting to use a destroyed detector which is no longer active");
+            if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                throw new IllegalDetectorStateException(
+                        "attempting to use a destroyed detector which is no longer active");
+            }
             throw new IllegalStateException(
                     "attempting to use a destroyed detector which is no longer active");
         }
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index bc42da6..d01e7fe 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
 import static android.Manifest.permission.RECORD_AUDIO;
+import static android.service.voice.VoiceInteractionService.MULTIPLE_ACTIVE_HOTWORD_DETECTORS;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -27,6 +28,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
+import android.app.compat.CompatChanges;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -50,9 +52,11 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SharedMemory;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IHotwordRecognitionStatusCallback;
 import com.android.internal.app.IVoiceInteractionManagerService;
 import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
@@ -62,8 +66,11 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * A class that lets a VoiceInteractionService implementation interact with
@@ -275,11 +282,12 @@
      * The metadata of the Keyphrase, derived from the enrollment application.
      * This may be null if this keyphrase isn't supported by the enrollment application.
      */
+    @GuardedBy("mLock")
     @Nullable
     private KeyphraseMetadata mKeyphraseMetadata;
     private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
     private final IVoiceInteractionManagerService mModelManagementService;
-    private final IVoiceInteractionSoundTriggerSession mSoundTriggerSession;
+    private IVoiceInteractionSoundTriggerSession mSoundTriggerSession;
     private final SoundTriggerListener mInternalCallback;
     private final Callback mExternalCallback;
     private final Handler mHandler;
@@ -287,6 +295,9 @@
     private final int mTargetSdkVersion;
     private final boolean mSupportHotwordDetectionService;
 
+    @GuardedBy("mLock")
+    private boolean mIsAvailabilityOverriddenByTestApi = false;
+    @GuardedBy("mLock")
     private int mAvailability = STATE_NOT_READY;
 
     /**
@@ -788,8 +799,7 @@
     public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
             KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
             IVoiceInteractionManagerService modelManagementService, int targetSdkVersion,
-            boolean supportHotwordDetectionService, @Nullable PersistableBundle options,
-            @Nullable SharedMemory sharedMemory) {
+            boolean supportHotwordDetectionService) {
         super(modelManagementService, callback,
                 supportHotwordDetectionService ? DETECTOR_TYPE_TRUSTED_HOTWORD_DSP
                         : DETECTOR_TYPE_NORMAL);
@@ -803,6 +813,12 @@
         mModelManagementService = modelManagementService;
         mTargetSdkVersion = targetSdkVersion;
         mSupportHotwordDetectionService = supportHotwordDetectionService;
+    }
+
+    @Override
+    void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
+        // TODO: transition to use an API that is not updateState to provide
+        //  onHotwordDetectionServiceInitialized status to external callback
         if (mSupportHotwordDetectionService) {
             updateStateLocked(options, sharedMemory, mInternalCallback,
                     DETECTOR_TYPE_TRUSTED_HOTWORD_DSP);
@@ -810,30 +826,40 @@
         try {
             Identity identity = new Identity();
             identity.packageName = ActivityThread.currentOpPackageName();
-            mSoundTriggerSession = mModelManagementService.createSoundTriggerSessionAsOriginator(
-                    identity, mBinder);
+            mSoundTriggerSession =
+                    mModelManagementService.createSoundTriggerSessionAsOriginator(
+                            identity, mBinder);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
-        new RefreshAvailabiltyTask().execute();
+        new RefreshAvailabilityTask().execute();
     }
 
     /**
      * {@inheritDoc}
      *
-     * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a
-     * {@link HotwordDetectionService} when it was created. In addition, if this
-     * AlwaysOnHotwordDetector is in an invalid or error state.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above and this AlwaysOnHotwordDetector wasn't specified to use a
+     *         {@link HotwordDetectionService} when it was created. In addition, the exception can
+     *         be thrown if this AlwaysOnHotwordDetector is in an invalid or error state.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if
+     *         this AlwaysOnHotwordDetector wasn't specified to use a
+     *         {@link HotwordDetectionService} when it was created. In addition, the exception can
+     *         be thrown if this AlwaysOnHotwordDetector is in an invalid or error state.
      */
     @Override
     public final void updateState(@Nullable PersistableBundle options,
-            @Nullable SharedMemory sharedMemory) {
+            @Nullable SharedMemory sharedMemory) throws IllegalDetectorStateException {
         synchronized (mLock) {
             if (!mSupportHotwordDetectionService) {
                 throw new IllegalStateException(
                         "updateState called, but it doesn't support hotword detection service");
             }
             if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                    throw new IllegalDetectorStateException(
+                            "updateState called on an invalid detector or error state");
+                }
                 throw new IllegalStateException(
                         "updateState called on an invalid detector or error state");
             }
@@ -843,6 +869,48 @@
     }
 
     /**
+     * Test API for manipulating the voice engine and sound model availability.
+     *
+     * After overriding the availability status, the client's
+     * {@link Callback#onAvailabilityChanged(int)} will be called to reflect the updated state.
+     *
+     * When this override is set, all system updates to availability will be ignored.
+     * @hide
+     */
+    @TestApi
+    public void overrideAvailability(int availability) {
+        synchronized (mLock) {
+            // ENROLLED state requires there to be metadata about the sound model so a fake one
+            // is created.
+            if (mKeyphraseMetadata == null && availability == STATE_KEYPHRASE_ENROLLED) {
+                Set<Locale> fakeSupportedLocales = new HashSet<>();
+                fakeSupportedLocales.add(mLocale);
+                mKeyphraseMetadata = new KeyphraseMetadata(1, mText, fakeSupportedLocales,
+                        AlwaysOnHotwordDetector.RECOGNITION_MODE_VOICE_TRIGGER);
+            }
+
+            mAvailability = availability;
+            mIsAvailabilityOverriddenByTestApi = true;
+            notifyStateChangedLocked();
+        }
+    }
+
+    /**
+     * Test API for clearing an availability override set by {@link #overrideAvailability(int)}
+     *
+     * This method will restore the availability to the current system state.
+     * @hide
+     */
+    @TestApi
+    public void resetAvailability() {
+        synchronized (mLock) {
+            mIsAvailabilityOverriddenByTestApi = false;
+        }
+        // Execute a refresh availability task - which should then notify of a change.
+        new RefreshAvailabilityTask().execute();
+    }
+
+    /**
      * Test API to simulate to trigger hardware recognition event for test.
      *
      * @hide
@@ -878,28 +946,46 @@
      * @see #RECOGNITION_MODE_USER_IDENTIFICATION
      * @see #RECOGNITION_MODE_VOICE_TRIGGER
      *
-     * @throws UnsupportedOperationException if the keyphrase itself isn't supported.
-     *         Callers should only call this method after a supported state callback on
-     *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid or error state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above. Because the caller receives availability updates via an asynchronous
+     *         callback, it may be due to the availability changing while this call is performed.
+     *         - Throws if the detector is in an invalid or error state.
+     *           This may happen if another detector has been instantiated or the
+     *           {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
+     *         33 Android if the recognition isn't supported. Callers should only call this method
+     *         after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
+     *         avoid this exception.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below Android API level
+     *         33 if the detector is in an invalid or error state. This may happen if another
+     *         detector has been instantiated or the {@link VoiceInteractionService} hosting this
+     *         detector has been shut down.
      */
-    public @RecognitionModes int getSupportedRecognitionModes() {
+    public @RecognitionModes
+    int getSupportedRecognitionModes() throws IllegalDetectorStateException {
         if (DBG) Slog.d(TAG, "getSupportedRecognitionModes()");
         synchronized (mLock) {
             return getSupportedRecognitionModesLocked();
         }
     }
 
-    private int getSupportedRecognitionModesLocked() {
+    @GuardedBy("mLock")
+    private int getSupportedRecognitionModesLocked() throws IllegalDetectorStateException {
         if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+            if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                throw new IllegalDetectorStateException("getSupportedRecognitionModes called on an"
+                        + " invalid detector or error state");
+            }
             throw new IllegalStateException(
                     "getSupportedRecognitionModes called on an invalid detector or error state");
         }
 
         // This method only makes sense if we can actually support a recognition.
         if (mAvailability != STATE_KEYPHRASE_ENROLLED || mKeyphraseMetadata == null) {
+            if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                throw new IllegalDetectorStateException("Getting supported recognition modes for"
+                        + " the keyphrase is not supported");
+            }
             throw new UnsupportedOperationException(
                     "Getting supported recognition modes for the keyphrase is not supported");
         }
@@ -926,6 +1012,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private int getSupportedAudioCapabilitiesLocked() {
         try {
             ModuleProperties properties =
@@ -949,30 +1036,77 @@
      * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
      *
      * @param recognitionFlags The flags to control the recognition properties.
+     * @param data Additional pass-through data to the system voice engine along with the
+     *             startRecognition request. This data is intended to provide additional parameters
+     *             when starting the opaque sound model.
      * @return Indicates whether the call succeeded or not.
-     * @throws UnsupportedOperationException if the recognition isn't supported.
-     *         Callers should only call this method after a supported state callback on
-     *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid or error state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above and attempts to start a recognition when the detector is not able based on
+     *         the availability state. This can be thrown even if the state has been checked before
+     *         calling this method because the caller receives availability updates via an
+     *         asynchronous callback, it may be due to the availability changing while this call is
+     *         performed.
+     *         - Throws if the recognition isn't supported.
+     *           Callers should only call this method after a supported state callback on
+     *           {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+     *         - Also throws if the detector is in an invalid or error state.
+     *           This may happen if another detector has been instantiated or the
+     *           {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
+     *         33 Android if the recognition isn't supported. Callers should only call this method
+     *         after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
+     *         avoid this exception.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below Android API level
+     *         33 if the detector is in an invalid or error state. This may happen if another
+     *         detector has been instantiated or the {@link VoiceInteractionService} hosting this
+     *         detector has been shut down.
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
-    public boolean startRecognition(@RecognitionFlags int recognitionFlags) {
+    public boolean startRecognition(@RecognitionFlags int recognitionFlags, @NonNull byte[] data)
+            throws IllegalDetectorStateException {
+        synchronized (mLock) {
+            return startRecognitionLocked(recognitionFlags, data)
+                    == STATUS_OK;
+        }
+    }
+
+    /**
+     * Starts recognition for the associated keyphrase.
+     * Caller must be the active voice interaction service via
+     * Settings.Secure.VOICE_INTERACTION_SERVICE.
+     *
+     * @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+     * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
+     *
+     * @param recognitionFlags The flags to control the recognition properties.
+     * @return Indicates whether the call succeeded or not.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above and attempts to start a recognition when the detector is not able based on
+     *         the availability state. This can be thrown even if the state has been checked before
+     *         calling this method because the caller receives availability updates via an
+     *         asynchronous callback, it may be due to the availability changing while this call is
+     *         performed.
+     *         - Throws if the recognition isn't supported.
+     *           Callers should only call this method after a supported state callback on
+     *           {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+     *         - Also throws if the detector is in an invalid or error state.
+     *           This may happen if another detector has been instantiated or the
+     *           {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
+     *         33 if the recognition isn't supported. Callers should only call this method after a
+     *         supported state callback on {@link Callback#onAvailabilityChanged(int)} to avoid this
+     *         exception.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
+     *         detector is in an invalid or error state. This may happen if another detector has
+     *         been instantiated or the {@link VoiceInteractionService} hosting this detector has
+     *         been shut down.
+     */
+    @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
+    public boolean startRecognition(@RecognitionFlags int recognitionFlags)
+            throws IllegalDetectorStateException {
         if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")");
         synchronized (mLock) {
-            if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
-                throw new IllegalStateException(
-                        "startRecognition called on an invalid detector or error state");
-            }
-
-            // Check if we can start/stop a recognition.
-            if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
-                throw new UnsupportedOperationException(
-                        "Recognition for the given keyphrase is not supported");
-            }
-
-            return startRecognitionLocked(recognitionFlags) == STATUS_OK;
+            return startRecognitionLocked(recognitionFlags, null /* data */) == STATUS_OK;
         }
     }
 
@@ -983,7 +1117,8 @@
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
     @Override
-    public boolean startRecognition() {
+    public boolean startRecognition()
+            throws IllegalDetectorStateException {
         return startRecognition(0);
     }
 
@@ -993,28 +1128,44 @@
      * Settings.Secure.VOICE_INTERACTION_SERVICE.
      *
      * @return Indicates whether the call succeeded or not.
-     * @throws UnsupportedOperationException if the recognition isn't supported.
-     *         Callers should only call this method after a supported state callback on
-     *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid or error state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of
+     *         API level 33 or above and attempts to stop a recognition when the detector is
+     *         not able based on the state. This can be thrown even if the state has been checked
+     *         before calling this method because the caller receives availability updates via an
+     *         asynchronous callback, it may be due to the availability changing while this call is
+     *         performed.
+     * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
+     *         33 if the recognition isn't supported. Callers should only call this method after a
+     *         supported state callback on {@link Callback#onAvailabilityChanged(int)} to avoid this
+     *         exception.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
+     *         detector is in an invalid or error state. This may happen if another detector has
+     *         been instantiated or the {@link VoiceInteractionService} hosting this detector has
+     *         been shut down.
      */
     // TODO: Remove this RequiresPermission since it isn't actually enforced. Also fix the javadoc
     // about permissions enforcement (when it throws vs when it just returns false) for other
     // methods in this class.
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
     @Override
-    public boolean stopRecognition() {
+    public boolean stopRecognition() throws IllegalDetectorStateException {
         if (DBG) Slog.d(TAG, "stopRecognition()");
         synchronized (mLock) {
             if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                    throw new IllegalDetectorStateException(
+                            "stopRecognition called on an invalid detector or error state");
+                }
                 throw new IllegalStateException(
                         "stopRecognition called on an invalid detector or error state");
             }
 
             // Check if we can start/stop a recognition.
             if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
+                if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                    throw new IllegalDetectorStateException(
+                            "Recognition for the given keyphrase is not supported");
+                }
                 throw new UnsupportedOperationException(
                         "Recognition for the given keyphrase is not supported");
             }
@@ -1039,18 +1190,28 @@
      *         - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
      *           if API is not supported by HAL
-     * @throws IllegalStateException if the detector is in an invalid or error state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         if the detector is in an invalid or error state. This may happen if another detector
+     *         has been instantiated or the {@link VoiceInteractionService} hosting this detector
+     *         has been shut down.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
+     *         detector is in an invalid or error state. This may happen if another detector has
+     *         been instantiated or the {@link VoiceInteractionService} hosting this detector has
+     *         been shut down.
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
-    public int setParameter(@ModelParams int modelParam, int value) {
+    public int setParameter(@ModelParams int modelParam, int value)
+            throws IllegalDetectorStateException {
         if (DBG) {
             Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")");
         }
 
         synchronized (mLock) {
             if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                    throw new IllegalDetectorStateException(
+                            "setParameter called on an invalid detector or error state");
+                }
                 throw new IllegalStateException(
                         "setParameter called on an invalid detector or error state");
             }
@@ -1071,18 +1232,27 @@
      *
      * @param modelParam   {@link ModelParams}
      * @return value of parameter
-     * @throws IllegalStateException if the detector is in an invalid or error state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         if the detector is in an invalid or error state. This may happen if another detector
+     *         has been instantiated or the {@link VoiceInteractionService} hosting this detector
+     *         has been shut down.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if
+     *         the detector is in an invalid or error state. This may happen if another detector has
+     *         been instantiated or the {@link VoiceInteractionService} hosting this detector has
+     *         been shut down.
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
-    public int getParameter(@ModelParams int modelParam) {
+    public int getParameter(@ModelParams int modelParam) throws IllegalDetectorStateException {
         if (DBG) {
             Slog.d(TAG, "getParameter(" + modelParam + ")");
         }
 
         synchronized (mLock) {
             if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                    throw new IllegalDetectorStateException(
+                            "getParameter called on an invalid detector or error state");
+                }
                 throw new IllegalStateException(
                         "getParameter called on an invalid detector or error state");
             }
@@ -1100,19 +1270,29 @@
      *
      * @param modelParam {@link ModelParams}
      * @return supported range of parameter, null if not supported
-     * @throws IllegalStateException if the detector is in an invalid or error state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         if the detector is in an invalid or error state. This may happen if another detector
+     *         has been instantiated or the {@link VoiceInteractionService} hosting this detector
+     *         has been shut down.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if
+     *         the detector is in an invalid or error state. This may happen if another detector has
+     *         been instantiated or the {@link VoiceInteractionService} hosting this detector has
+     *         been shut down.
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
     @Nullable
-    public ModelParamRange queryParameter(@ModelParams int modelParam) {
+    public ModelParamRange queryParameter(@ModelParams int modelParam)
+            throws IllegalDetectorStateException {
         if (DBG) {
             Slog.d(TAG, "queryParameter(" + modelParam + ")");
         }
 
         synchronized (mLock) {
             if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                    throw new IllegalDetectorStateException(
+                            "queryParameter called on an invalid detector or error state");
+                }
                 throw new IllegalStateException(
                         "queryParameter called on an invalid detector or error state");
             }
@@ -1129,15 +1309,25 @@
      * otherwise {@link #createReEnrollIntent()} should be preferred.
      *
      * @return An {@link Intent} to start enrollment for the given keyphrase.
-     * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
-     *         Callers should only call this method after a supported state callback on
-     *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above.
+     *         - Thrown if managing they keyphrase isn't supported. Callers should only call this
+     *           method after a supported state callback on
+     *           {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+     *         - Thrown if the detector is in an invalid state. This may happen if another detector
+     *           has been instantiated or the {@link VoiceInteractionService} hosting this detector
+     *           has been shut down.
+     * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
+     *         33 if managing they keyphrase isn't supported. Callers should only call this method
+     *         after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
+     *         avoid this exception.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
+     *         detector is in an invalid state. This may happen if another detector has been
+     *         instantiated or the {@link VoiceInteractionService} hosting this detector has been
+     *         shut down.
      */
     @Nullable
-    public Intent createEnrollIntent() {
+    public Intent createEnrollIntent() throws IllegalDetectorStateException {
         if (DBG) Slog.d(TAG, "createEnrollIntent");
         synchronized (mLock) {
             return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_ENROLL);
@@ -1151,15 +1341,25 @@
      * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
      *
      * @return An {@link Intent} to start un-enrollment for the given keyphrase.
-     * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
-     *         Callers should only call this method after a supported state callback on
-     *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above.
+     *         - Thrown if managing they keyphrase isn't supported. Callers should only call this
+     *           method after a supported state callback on
+     *           {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+     *         - Thrown if the detector is in an invalid state. This may happen if another detector
+     *           has been instantiated or the {@link VoiceInteractionService} hosting this detector
+     *           has been shut down.
+     * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
+     *         33 if managing they keyphrase isn't supported. Callers should only call this method
+     *         after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
+     *         avoid this exception.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
+     *         detector is in an invalid state. This may happen if another detector has been
+     *         instantiated or the {@link VoiceInteractionService} hosting this detector has been
+     *         shut down.
      */
     @Nullable
-    public Intent createUnEnrollIntent() {
+    public Intent createUnEnrollIntent() throws IllegalDetectorStateException {
         if (DBG) Slog.d(TAG, "createUnEnrollIntent");
         synchronized (mLock) {
             return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_UN_ENROLL);
@@ -1173,30 +1373,50 @@
      * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
      *
      * @return An {@link Intent} to start re-enrollment for the given keyphrase.
-     * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
-     *         Callers should only call this method after a supported state callback on
-     *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid or error state.
-     *         This may happen if another detector has been instantiated or the
-     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above.
+     *         - Thrown if managing they keyphrase isn't supported. Callers should only call this
+     *           method after a supported state callback on
+     *           {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+     *         - Thrown if the detector is in an invalid state. This may happen if another detector
+     *           has been instantiated or the {@link VoiceInteractionService} hosting this detector
+     *           has been shut down.
+     * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
+     *         33 if managing they keyphrase isn't supported. Callers should only call this method
+     *         after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
+     *         avoid this exception.
+     * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
+     *         detector is in an invalid state. This may happen if another detector has been
+     *         instantiated or the {@link VoiceInteractionService} hosting this detector has been
+     *         shut down.
      */
     @Nullable
-    public Intent createReEnrollIntent() {
+    public Intent createReEnrollIntent() throws IllegalDetectorStateException {
         if (DBG) Slog.d(TAG, "createReEnrollIntent");
         synchronized (mLock) {
             return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_RE_ENROLL);
         }
     }
 
-    private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action) {
+    @GuardedBy("mLock")
+    private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action)
+            throws IllegalDetectorStateException {
         if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+            if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                throw new IllegalDetectorStateException(
+                        "getManageIntent called on an invalid detector or error state");
+            }
             throw new IllegalStateException(
-                    "getManageIntent called on an invalid detector or error state");
+                "getManageIntent called on an invalid detector or error state");
         }
 
         // This method only makes sense if we can actually support a recognition.
         if (mAvailability != STATE_KEYPHRASE_ENROLLED
                 && mAvailability != STATE_KEYPHRASE_UNENROLLED) {
+            if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                throw new IllegalDetectorStateException(
+                        "Managing the given keyphrase is not supported");
+            }
             throw new UnsupportedOperationException(
                     "Managing the given keyphrase is not supported");
         }
@@ -1212,24 +1432,29 @@
     public void destroy() {
         synchronized (mLock) {
             if (mAvailability == STATE_KEYPHRASE_ENROLLED) {
-                stopRecognition();
+                try {
+                    stopRecognition();
+                } catch (Exception e) {
+                    Log.i(TAG, "failed to stopRecognition in destroy", e);
+                }
             }
 
             mAvailability = STATE_INVALID;
+            mIsAvailabilityOverriddenByTestApi = false;
             notifyStateChangedLocked();
-
-            if (mSupportHotwordDetectionService) {
-                try {
-                    mModelManagementService.shutdownHotwordDetectionService();
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
         }
         super.destroy();
     }
 
     /**
+     * @hide
+     */
+    @Override
+    public boolean isUsingHotwordDetectionService() {
+        return mSupportHotwordDetectionService;
+    }
+
+    /**
      * Reloads the sound models from the service.
      *
      * @hide
@@ -1244,6 +1469,15 @@
                 return;
             }
 
+            // Because this method reflects an update from the system service models, we should not
+            // update the client of an availability change when the availability has been overridden
+            // via a test API.
+            if (mIsAvailabilityOverriddenByTestApi) {
+                Slog.w(TAG, "Suppressing system availability update. "
+                        + "Availability is overridden by test API.");
+                return;
+            }
+
             // Stop the recognition before proceeding.
             // This is done because we want to stop the recognition on an older model if it changed
             // or was deleted.
@@ -1263,11 +1497,37 @@
             }
 
             // Execute a refresh availability task - which should then notify of a change.
-            new RefreshAvailabiltyTask().execute();
+            new RefreshAvailabilityTask().execute();
         }
     }
 
-    private int startRecognitionLocked(int recognitionFlags) {
+    @GuardedBy("mLock")
+    private int startRecognitionLocked(int recognitionFlags,
+            @Nullable byte[] data) throws IllegalDetectorStateException {
+        if (DBG) {
+            Slog.d(TAG, "startRecognition("
+                    + recognitionFlags
+                    + ", " + Arrays.toString(data) + ")");
+        }
+        if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+            if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                throw new IllegalDetectorStateException(
+                        "startRecognition called on an invalid detector or error state");
+            }
+            throw new IllegalStateException(
+                    "startRecognition called on an invalid detector or error state");
+        }
+
+        // Check if we can start/stop a recognition.
+        if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
+            if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
+                throw new IllegalDetectorStateException(
+                        "Recognition for the given keyphrase is not supported");
+            }
+            throw new UnsupportedOperationException(
+                    "Recognition for the given keyphrase is not supported");
+        }
+
         KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1];
         // TODO: Do we need to do something about the confidence level here?
         recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.getId(),
@@ -1291,7 +1551,7 @@
             code = mSoundTriggerSession.startRecognition(
                     mKeyphraseMetadata.getId(), mLocale.toLanguageTag(), mInternalCallback,
                     new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                            recognitionExtra, null /* additional data */, audioCapabilities),
+                            recognitionExtra, data, audioCapabilities),
                     runInBatterySaver);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1303,6 +1563,7 @@
         return code;
     }
 
+    @GuardedBy("mLock")
     private int stopRecognitionLocked() {
         int code;
         try {
@@ -1318,6 +1579,7 @@
         return code;
     }
 
+    @GuardedBy("mLock")
     private int setParameterLocked(@ModelParams int modelParam, int value) {
         try {
             int code = mSoundTriggerSession.setParameter(mKeyphraseMetadata.getId(), modelParam,
@@ -1333,6 +1595,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private int getParameterLocked(@ModelParams int modelParam) {
         try {
             return mSoundTriggerSession.getParameter(mKeyphraseMetadata.getId(), modelParam);
@@ -1341,6 +1604,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     @Nullable
     private ModelParamRange queryParameterLocked(@ModelParams int modelParam) {
         try {
@@ -1357,15 +1621,19 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void updateAndNotifyStateChangedLocked(int availability) {
         if (DBG) {
             Slog.d(TAG, "Hotword availability changed from " + mAvailability
                     + " -> " + availability);
         }
-        mAvailability = availability;
+        if (!mIsAvailabilityOverriddenByTestApi) {
+            mAvailability = availability;
+        }
         notifyStateChangedLocked();
     }
 
+    @GuardedBy("mLock")
     private void notifyStateChangedLocked() {
         Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
         message.arg1 = mAvailability;
@@ -1487,7 +1755,7 @@
         }
     }
 
-    class RefreshAvailabiltyTask extends AsyncTask<Void, Void, Void> {
+    class RefreshAvailabilityTask extends AsyncTask<Void, Void, Void> {
 
         @Override
         public Void doInBackground(Void... params) {
@@ -1555,7 +1823,26 @@
         }
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) {
+            if (!(obj instanceof AlwaysOnHotwordDetector)) {
+                return false;
+            }
+            AlwaysOnHotwordDetector other = (AlwaysOnHotwordDetector) obj;
+            return TextUtils.equals(mText, other.mText) && mLocale.equals(other.mLocale);
+        }
+
+        return super.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mText, mLocale);
+    }
+
     /** @hide */
+    @Override
     public void dump(String prefix, PrintWriter pw) {
         synchronized (mLock) {
             pw.print(prefix); pw.print("Text="); pw.println(mText);
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index dfe0f54..19e248f 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -243,13 +243,26 @@
     /**
      * Called when the device hardware (such as a DSP) detected the hotword, to request second stage
      * validation before handing over the audio to the {@link AlwaysOnHotwordDetector}.
-     * <p>
-     * After {@code callback} is invoked or {@code timeoutMillis} has passed, and invokes the
+     *
+     * <p>After {@code callback} is invoked or {@code timeoutMillis} has passed, and invokes the
      * appropriate {@link AlwaysOnHotwordDetector.Callback callback}.
      *
+     * <p>When responding to a detection event, the
+     * {@link HotwordDetectedResult#getHotwordPhraseId()} must match a keyphrase ID listed
+     * in the eventPayload's
+     * {@link AlwaysOnHotwordDetector.EventPayload#getKeyphraseRecognitionExtras()} list. This is
+     * forcing the intention of the {@link HotwordDetectionService} to validate an event from the
+     * voice engine and not augment its result.
+     *
      * @param eventPayload Payload data for the hardware detection event. This may contain the
-     *                     trigger audio, if requested when calling
-     *                     {@link AlwaysOnHotwordDetector#startRecognition(int)}.
+     *             trigger audio, if requested when calling
+     *             {@link AlwaysOnHotwordDetector#startRecognition(int)}.
+     *             Each {@link AlwaysOnHotwordDetector} will be associated with at minimum a unique
+     *             keyphrase ID indicated by
+     *             {@link AlwaysOnHotwordDetector.EventPayload#getKeyphraseRecognitionExtras()}[0].
+     *             Any extra
+     *             {@link android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra}'s
+     *             in the eventPayload represent additional phrases detected by the voice engine.
      * @param timeoutMillis Timeout in milliseconds for the operation to invoke the callback. If
      *                      the application fails to abide by the timeout, system will close the
      *                      microphone and cancel the operation.
diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java
index 96fd8bb..1a0dc89 100644
--- a/core/java/android/service/voice/HotwordDetector.java
+++ b/core/java/android/service/voice/HotwordDetector.java
@@ -23,10 +23,16 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.media.AudioFormat;
+import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.SharedMemory;
+import android.util.AndroidException;
+
+import java.io.PrintWriter;
 
 /**
  * Basic functionality for hotword detectors.
@@ -37,6 +43,23 @@
 public interface HotwordDetector {
 
     /**
+     * Prior to API level 33, API calls of {@link android.service.voice.HotwordDetector} could
+     * return both {@link java.lang.IllegalStateException} or
+     * {@link java.lang.UnsupportedOperationException} depending on the detector's underlying state.
+     * This lead to confusing behavior as the underlying state of the detector can be modified
+     * without the knowledge of the caller via system service layer updates.
+     *
+     * This change ID, when enabled, changes the API calls to only throw checked exception
+     * {@link android.service.voice.HotwordDetector.IllegalDetectorStateException} when checking
+     * against state information modified by both the caller and the system services.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    long HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION = 226355112L;
+
+    /**
      * Indicates that it is a non-trusted hotword detector.
      *
      * @hide
@@ -74,16 +97,26 @@
      * Calling this again while recognition is active does nothing.
      *
      * @return true if the request to start recognition succeeded
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above and attempts to start a recognition when the detector is not able based on
+     *         the state. This can be thrown even if the state has been checked before calling this
+     *         method because the caller receives updates via an asynchronous callback, and the
+     *         state of the detector can change concurrently to the caller calling this method.
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
-    boolean startRecognition();
+    boolean startRecognition() throws IllegalDetectorStateException;
 
     /**
      * Stops hotword recognition.
      *
      * @return true if the request to stop recognition succeeded
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above and attempts to stop a recognition when the detector is not able based on
+     *         the state. This can be thrown even if the state has been checked before calling this
+     *         method because the caller receives updates via an asynchronous callback, and the
+     *         state of the detector can change concurrently to the caller calling this method.
      */
-    boolean stopRecognition();
+    boolean stopRecognition() throws IllegalDetectorStateException;
 
     /**
      * Starts hotword recognition on audio coming from an external connected microphone.
@@ -97,26 +130,37 @@
      *         PersistableBundle does not allow any remotable objects or other contents that can be
      *         used to communicate with other processes.
      * @return true if the request to start recognition succeeded
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above and attempts to start a recognition when the detector is not able based on
+     *         the state. This can be thrown even if the state has been checked before calling this
+     *         method because the caller receives updates via an asynchronous callback, and the
+     *         state of the detector can change concurrently to the caller calling this method.
      */
     boolean startRecognition(
             @NonNull ParcelFileDescriptor audioStream,
             @NonNull AudioFormat audioFormat,
-            @Nullable PersistableBundle options);
+            @Nullable PersistableBundle options) throws IllegalDetectorStateException;
 
     /**
      * Set configuration and pass read-only data to hotword detection service.
      *
      * @param options Application configuration data to provide to the
-     * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
-     * other contents that can be used to communicate with other processes.
+     *         {@link HotwordDetectionService}. PersistableBundle does not allow any remotable
+     *         objects or other contents that can be used to communicate with other processes.
      * @param sharedMemory The unrestricted data blob to provide to the
-     * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
-     * such data to the trusted process.
-     *
+     *         {@link HotwordDetectionService}. Use this to provide the hotword models data or other
+     *         such data to the trusted process.
+     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
+     *         or above and the detector is not able to perform the operation based on the
+     *         underlying state. This can be thrown even if the state has been checked before
+     *         calling this method because the caller receives updates via an asynchronous callback,
+     *         and the state of the detector can change concurrently to the caller calling this
+     *         method.
      * @throws IllegalStateException if this HotwordDetector wasn't specified to use a
-     * {@link HotwordDetectionService} when it was created.
+     *         {@link HotwordDetectionService} when it was created.
      */
-    void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory);
+    void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory)
+            throws IllegalDetectorStateException;
 
     /**
      * Invalidates this hotword detector so that any future calls to this result
@@ -132,6 +176,13 @@
     /**
      * @hide
      */
+    default boolean isUsingHotwordDetectionService() {
+        throw new UnsupportedOperationException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * @hide
+     */
     static String detectorTypeToString(int detectorType) {
         switch (detectorType) {
             case DETECTOR_TYPE_NORMAL:
@@ -145,6 +196,11 @@
         }
     }
 
+    /** @hide */
+    default void dump(String prefix, PrintWriter pw) {
+        throw new UnsupportedOperationException("Not implemented. Must override in a subclass.");
+    }
+
     /**
      * The callback to notify of detection events.
      */
@@ -205,4 +261,14 @@
          */
         void onHotwordDetectionServiceRestarted();
     }
+
+    /**
+     * {@link HotwordDetector} specific exception thrown when the underlying state of the detector
+     * is invalid for the given action.
+     */
+    class IllegalDetectorStateException extends AndroidException {
+        IllegalDetectorStateException(String message) {
+            super(message);
+        }
+    }
 }
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 2d662ea..36ea91e 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -30,6 +30,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SharedMemory;
+import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.app.IHotwordRecognitionStatusCallback;
@@ -57,8 +58,6 @@
     SoftwareHotwordDetector(
             IVoiceInteractionManagerService managerService,
             AudioFormat audioFormat,
-            PersistableBundle options,
-            SharedMemory sharedMemory,
             HotwordDetector.Callback callback) {
         super(managerService, callback, DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE);
 
@@ -66,6 +65,12 @@
         mAudioFormat = audioFormat;
         mCallback = callback;
         mHandler = new Handler(Looper.getMainLooper());
+    }
+
+    @Override
+    void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
+        // TODO: transition to use an API that is not updateState to provide
+        //  onHotwordDetectionServiceInitialized status to external callback
         updateStateLocked(options, sharedMemory,
                 new InitializationStateListener(mHandler, mCallback),
                 DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE);
@@ -73,7 +78,7 @@
 
     @RequiresPermission(RECORD_AUDIO)
     @Override
-    public boolean startRecognition() {
+    public boolean startRecognition() throws IllegalDetectorStateException {
         if (DEBUG) {
             Slog.i(TAG, "#startRecognition");
         }
@@ -96,7 +101,7 @@
     /** TODO: stopRecognition */
     @RequiresPermission(RECORD_AUDIO)
     @Override
-    public boolean stopRecognition() {
+    public boolean stopRecognition() throws IllegalDetectorStateException {
         if (DEBUG) {
             Slog.i(TAG, "#stopRecognition");
         }
@@ -113,17 +118,23 @@
 
     @Override
     public void destroy() {
-        stopRecognition();
-        maybeCloseExistingSession();
-
         try {
-            mManagerService.shutdownHotwordDetectionService();
-        } catch (RemoteException ex) {
-            ex.rethrowFromSystemServer();
+            stopRecognition();
+        } catch (Exception e) {
+            Log.i(TAG, "failed to stopRecognition in destroy", e);
         }
+        maybeCloseExistingSession();
         super.destroy();
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isUsingHotwordDetectionService() {
+        return true;
+    }
+
     private void maybeCloseExistingSession() {
         // TODO: needs to be synchronized.
         // TODO: implement this
@@ -231,6 +242,7 @@
     }
 
     /** @hide */
+    @Override
     public void dump(String prefix, PrintWriter pw) {
         // TODO: implement this
     }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index bf0cfbe..1170237 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -24,12 +24,16 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
 import android.media.voice.KeyphraseModelManager;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -89,6 +93,37 @@
      */
     public static final String SERVICE_META_DATA = "android.voice_interaction";
 
+    /**
+     * For apps targeting Build.VERSION_CODES.TRAMISU and above, implementors of this
+     * service can create multiple AlwaysOnHotwordDetector instances in parallel. They will
+     * also e ale to create a single SoftwareHotwordDetector in parallel with any other
+     * active AlwaysOnHotwordDetector instances.
+     *
+     * <p>Requirements when this change is enabled:
+     * <ul>
+     *     <li>
+     *         Any number of AlwaysOnHotwordDetector instances can be created in parallel
+     *         as long as they are unique to any other active AlwaysOnHotwordDetector.
+     *     </li>
+     *     <li>
+     *         Only a single instance of SoftwareHotwordDetector can be active at a given
+     *         time. It can be active at the same time as any number of
+     *         AlwaysOnHotwordDetector instances.
+     *     </li>
+     *     <li>
+     *         To release that reference and any resources associated with that reference,
+     *         HotwordDetector#destroy() must be called. An attempt to create an
+     *         HotwordDetector equal to an active HotwordDetector will be rejected
+     *         until HotwordDetector#destroy() is called on the active instance.
+     *     </li>
+     * </ul>
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
+    static final long MULTIPLE_ACTIVE_HOTWORD_DETECTORS = 193232191L;
+
     IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
         @Override
         public void ready() {
@@ -133,8 +168,7 @@
 
     private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
 
-    private AlwaysOnHotwordDetector mHotwordDetector;
-    private SoftwareHotwordDetector mSoftwareHotwordDetector;
+    private final Set<HotwordDetector> mActiveHotwordDetectors = new ArraySet<>();
 
     /**
      * Called when a user has activated an affordance to launch voice assist from the Keyguard.
@@ -284,10 +318,12 @@
 
     private void onSoundModelsChangedInternal() {
         synchronized (this) {
-            if (mHotwordDetector != null) {
-                // TODO: Stop recognition if a sound model that was being recognized gets deleted.
-                mHotwordDetector.onSoundModelsChanged();
-            }
+            // TODO: Stop recognition if a sound model that was being recognized gets deleted.
+            mActiveHotwordDetectors.forEach(detector -> {
+                if (detector instanceof AlwaysOnHotwordDetector) {
+                    ((AlwaysOnHotwordDetector) detector).onSoundModelsChanged();
+                }
+            });
         }
     }
 
@@ -379,16 +415,31 @@
             throw new IllegalStateException("Not available until onReady() is called");
         }
         synchronized (mLock) {
-            // Allow only one concurrent recognition via the APIs.
-            safelyShutdownAllHotwordDetectors();
-            mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
-                    mKeyphraseEnrollmentInfo, mSystemService,
+            if (!CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) {
+                // Allow only one concurrent recognition via the APIs.
+                safelyShutdownAllHotwordDetectors();
+            }
+
+            AlwaysOnHotwordDetector dspDetector = new AlwaysOnHotwordDetector(keyphrase, locale,
+                    callback, mKeyphraseEnrollmentInfo, mSystemService,
                     getApplicationContext().getApplicationInfo().targetSdkVersion,
-                    supportHotwordDetectionService, options, sharedMemory);
-            mHotwordDetector.registerOnDestroyListener((detector) -> onDspHotwordDetectorDestroyed(
-                    (AlwaysOnHotwordDetector) detector));
+                    supportHotwordDetectionService);
+            if (!mActiveHotwordDetectors.add(dspDetector)) {
+                throw new IllegalArgumentException(
+                        "the keyphrase=" + keyphrase + " and locale=" + locale
+                                + " are already used by another always-on detector");
+            }
+
+            try {
+                dspDetector.registerOnDestroyListener(this::onHotwordDetectorDestroyed);
+                dspDetector.initialize(options, sharedMemory);
+            } catch (Exception e) {
+                mActiveHotwordDetectors.remove(dspDetector);
+                dspDetector.destroy();
+                throw e;
+            }
+            return dspDetector;
         }
-        return mHotwordDetector;
     }
 
     /**
@@ -434,16 +485,34 @@
             throw new IllegalStateException("Not available until onReady() is called");
         }
         synchronized (mLock) {
-            // Allow only one concurrent recognition via the APIs.
-            safelyShutdownAllHotwordDetectors();
-            mSoftwareHotwordDetector =
+            if (!CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) {
+                // Allow only one concurrent recognition via the APIs.
+                safelyShutdownAllHotwordDetectors();
+            } else {
+                for (HotwordDetector detector : mActiveHotwordDetectors) {
+                    if (detector instanceof SoftwareHotwordDetector) {
+                        throw new IllegalArgumentException(
+                                "There is already an active SoftwareHotwordDetector. "
+                                        + "It must be destroyed to create a new one.");
+                    }
+                }
+            }
+
+            SoftwareHotwordDetector softwareHotwordDetector =
                     new SoftwareHotwordDetector(
-                            mSystemService, null, options, sharedMemory, callback);
-            mSoftwareHotwordDetector.registerOnDestroyListener(
-                    (detector) -> onMicrophoneHotwordDetectorDestroyed(
-                            (SoftwareHotwordDetector) detector));
+                            mSystemService, null, callback);
+
+            try {
+                softwareHotwordDetector.registerOnDestroyListener(
+                        this::onHotwordDetectorDestroyed);
+                softwareHotwordDetector.initialize(options, sharedMemory);
+            } catch (Exception e) {
+                mActiveHotwordDetectors.remove(softwareHotwordDetector);
+                softwareHotwordDetector.destroy();
+                throw e;
+            }
+            return softwareHotwordDetector;
         }
-        return mSoftwareHotwordDetector;
     }
 
     /**
@@ -489,33 +558,34 @@
 
     private void safelyShutdownAllHotwordDetectors() {
         synchronized (mLock) {
-            if (mHotwordDetector != null) {
+            mActiveHotwordDetectors.forEach(detector -> {
                 try {
-                    mHotwordDetector.destroy();
+                    detector.destroy();
                 } catch (Exception ex) {
-                    Log.i(TAG, "exception destroying AlwaysOnHotwordDetector", ex);
+                    Log.i(TAG, "exception destroying HotwordDetector", ex);
                 }
-            }
-
-            if (mSoftwareHotwordDetector != null) {
-                try {
-                    mSoftwareHotwordDetector.destroy();
-                } catch (Exception ex) {
-                    Log.i(TAG, "exception destroying SoftwareHotwordDetector", ex);
-                }
-            }
+            });
         }
     }
 
-    private void onDspHotwordDetectorDestroyed(@NonNull AlwaysOnHotwordDetector detector) {
+    private void onHotwordDetectorDestroyed(@NonNull HotwordDetector detector) {
         synchronized (mLock) {
-            mHotwordDetector = null;
+            mActiveHotwordDetectors.remove(detector);
+            shutdownHotwordDetectionServiceIfRequiredLocked();
         }
     }
 
-    private void onMicrophoneHotwordDetectorDestroyed(@NonNull SoftwareHotwordDetector detector) {
-        synchronized (mLock) {
-            mSoftwareHotwordDetector = null;
+    private void shutdownHotwordDetectionServiceIfRequiredLocked() {
+        for (HotwordDetector detector : mActiveHotwordDetectors) {
+            if (detector.isUsingHotwordDetectionService()) {
+                return;
+            }
+        }
+
+        try {
+            mSystemService.shutdownHotwordDetectionService();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -540,18 +610,14 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("VOICE INTERACTION");
         synchronized (mLock) {
-            pw.println("  AlwaysOnHotwordDetector");
-            if (mHotwordDetector == null) {
+            pw.println("  HotwordDetector(s)");
+            if (mActiveHotwordDetectors.size() == 0) {
                 pw.println("    NULL");
             } else {
-                mHotwordDetector.dump("    ", pw);
-            }
-
-            pw.println("  MicrophoneHotwordDetector");
-            if (mSoftwareHotwordDetector == null) {
-                pw.println("    NULL");
-            } else {
-                mSoftwareHotwordDetector.dump("    ", pw);
+                mActiveHotwordDetectors.forEach(detector -> {
+                    detector.dump("    ", pw);
+                    pw.println();
+                });
             }
         }
     }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 2f85d2b6..520ceb2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -25,6 +25,7 @@
 import android.graphics.text.LineBreakConfig;
 import android.graphics.text.LineBreaker;
 import android.os.Build;
+import android.os.SystemProperties;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
 import android.text.style.LineHeightSpan;
@@ -32,6 +33,7 @@
 import android.util.Log;
 import android.util.Pools.SynchronizedPool;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 
@@ -73,6 +75,13 @@
      * default values.
      */
     public final static class Builder {
+        // The content length threshold to enable LINE_BREAK_WORD_STYLE_PHRASE.
+        private static final int DEFAULT_LINECOUNT_THRESHOLD_FOR_PHRASE = 3;
+
+        // The property of content length threshold to enable LINE_BREAK_WORD_STYLE_PHRASE.
+        private static final String PROPERTY_LINECOUNT_THRESHOLD_FOR_PHRASE =
+                "android.phrase.linecount.threshold";
+
         private Builder() {}
 
         /**
@@ -431,11 +440,55 @@
          */
         @NonNull
         public StaticLayout build() {
+            reviseLineBreakConfig();
             StaticLayout result = new StaticLayout(this);
             Builder.recycle(this);
             return result;
         }
 
+        private void reviseLineBreakConfig() {
+            boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking();
+            int wordStyle = mLineBreakConfig.getLineBreakWordStyle();
+            if (autoPhraseBreaking) {
+                if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) {
+                    if (shouldEnablePhraseBreaking()) {
+                        mLineBreakConfig = LineBreakConfig.getLineBreakConfig(
+                                mLineBreakConfig.getLineBreakStyle(),
+                                LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE,
+                                mLineBreakConfig.getAutoPhraseBreaking());
+                    }
+                }
+            }
+        }
+
+        private boolean shouldEnablePhraseBreaking() {
+            if (TextUtils.isEmpty(mText) || mWidth <= 0) {
+                return false;
+            }
+            int lineLimit = SystemProperties.getInt(
+                    PROPERTY_LINECOUNT_THRESHOLD_FOR_PHRASE,
+                    DEFAULT_LINECOUNT_THRESHOLD_FOR_PHRASE);
+            double desiredWidth = (double) Layout.getDesiredWidth(mText, mStart,
+                    mEnd, mPaint, mTextDir);
+            int lineCount = (int) Math.ceil(desiredWidth / mWidth);
+            if (lineCount > 0 && lineCount <= lineLimit) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Get the line break word style.
+         *
+         * @return The current line break word style.
+         *
+         * @hide
+         */
+        @VisibleForTesting
+        public int getLineBreakWordStyle() {
+            return mLineBreakConfig.getLineBreakWordStyle();
+        }
+
         private CharSequence mText;
         private int mStart;
         private int mEnd;
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 7ac6ae1..51e3665 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -70,7 +70,6 @@
 import android.util.Printer;
 import android.view.View;
 
-import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
@@ -1230,8 +1229,8 @@
 
     /**
      * Transforms a CharSequences to uppercase, copying the sources spans and keeping them spans as
-     * much as possible close to their relative original places. In the case the the uppercase
-     * string is identical to the sources, the source itself is returned instead of being copied.
+     * much as possible close to their relative original places. If uppercase string is identical
+     * to the sources, the source itself is returned instead of being copied.
      *
      * If copySpans is set, source must be an instance of Spanned.
      *
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 3be4c3ed..a173d80 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -74,6 +74,14 @@
     public static final String SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE =
             "settings_hide_second_layer_page_navigate_up_button_in_two_pane";
 
+    /** @hide */
+    public static final String SETTINGS_AUTO_TEXT_WRAPPING = "settings_auto_text_wrapping";
+
+    /** Flag to enable/disable guest mode UX changes as mentioned in b/214031645
+     *  @hide
+     */
+    public static  final String SETTINGS_GUEST_MODE_UX_CHANGES = "settings_guest_mode_ux_changes";
+
     private static final Map<String, String> DEFAULT_FLAGS;
 
     static {
@@ -100,6 +108,8 @@
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
         DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
         DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
+        DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false");
+        DEFAULT_FLAGS.put(SETTINGS_GUEST_MODE_UX_CHANGES, "true");
     }
 
     private static final Set<String> PERSISTENT_FLAGS;
@@ -110,6 +120,7 @@
         PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
         PERSISTENT_FLAGS.add(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME);
         PERSISTENT_FLAGS.add(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE);
+        PERSISTENT_FLAGS.add(SETTINGS_AUTO_TEXT_WRAPPING);
     }
 
     /**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0c4d9bf..3a68404 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -26,11 +26,9 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
-import android.app.ActivityThread;
 import android.app.KeyguardManager;
 import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -52,14 +50,11 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 
-import com.android.internal.R;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Optional;
 
 /**
  * Provides information about the size and density of a logical display.
@@ -116,12 +111,6 @@
     private int mCachedAppHeightCompat;
 
     /**
-     * Cache if the application is the recents component.
-     * TODO(b/179308296) Remove once Launcher addresses issue
-     */
-    private Optional<Boolean> mIsRecentsComponent = Optional.empty();
-
-    /**
      * The default Display id, which is the id of the primary display assuming there is one.
      */
     public static final int DEFAULT_DISPLAY = 0;
@@ -576,7 +565,8 @@
      * @see com.android.service.display.DisplayDevice#hasStableUniqueId().
      * @hide
      */
-    public String getUniqueId() {
+    @TestApi
+    public @Nullable String getUniqueId() {
         return mDisplayInfo.uniqueId;
     }
 
@@ -1584,36 +1574,7 @@
             return false;
         }
         final Configuration config = mResources.getConfiguration();
-        // TODO(b/179308296) Temporarily exclude Launcher from being given max bounds, by checking
-        // if the caller is the recents component.
-        return config != null && !config.windowConfiguration.getMaxBounds().isEmpty()
-                && !isRecentsComponent();
-    }
-
-    /**
-     * Returns {@code true} when the calling package is the recents component.
-     * TODO(b/179308296) Remove once Launcher addresses issue
-     */
-    boolean isRecentsComponent() {
-        if (mIsRecentsComponent.isPresent()) {
-            return mIsRecentsComponent.get();
-        }
-        if (mResources == null) {
-            return false;
-        }
-        try {
-            String recentsComponent = mResources.getString(R.string.config_recentsComponentName);
-            if (recentsComponent == null) {
-                return false;
-            }
-            String recentsPackage = ComponentName.unflattenFromString(recentsComponent)
-                    .getPackageName();
-            mIsRecentsComponent = Optional.of(recentsPackage != null
-                    && recentsPackage.equals(ActivityThread.currentPackageName()));
-            return mIsRecentsComponent.get();
-        } catch (Resources.NotFoundException e) {
-            return false;
-        }
+        return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
     }
 
     /**
@@ -1631,6 +1592,21 @@
     }
 
     /**
+     * Returns the committed state of the display.
+     *
+     * @return The latest committed display state, such as {@link #STATE_ON}. The display state
+     * {@link Display#getState()} is set as committed only after power state changes finish.
+     *
+     * @hide
+     */
+    public int getCommittedState() {
+        synchronized (mLock) {
+            updateDisplayInfoLocked();
+            return mIsValid ? mDisplayInfo.committedState : STATE_UNKNOWN;
+        }
+    }
+
+    /**
      * Returns true if the specified UID has access to this display.
      * @hide
      */
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 9264d2e..2aed319 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -253,6 +253,12 @@
     public int state;
 
     /**
+     * The current committed state of the display. For example, this becomes
+     * {@link android.view.Display#STATE_ON} only after the power state ON is fully committed.
+     */
+    public int committedState;
+
+    /**
      * The UID of the application that owns this display, or zero if it is owned by the system.
      * <p>
      * If the display is private, then only the owner can use it.
@@ -380,6 +386,7 @@
                 && appVsyncOffsetNanos == other.appVsyncOffsetNanos
                 && presentationDeadlineNanos == other.presentationDeadlineNanos
                 && state == other.state
+                && committedState == other.committedState
                 && ownerUid == other.ownerUid
                 && Objects.equals(ownerPackageName, other.ownerPackageName)
                 && removeMode == other.removeMode
@@ -431,6 +438,7 @@
         appVsyncOffsetNanos = other.appVsyncOffsetNanos;
         presentationDeadlineNanos = other.presentationDeadlineNanos;
         state = other.state;
+        committedState = other.committedState;
         ownerUid = other.ownerUid;
         ownerPackageName = other.ownerPackageName;
         removeMode = other.removeMode;
@@ -482,6 +490,7 @@
         appVsyncOffsetNanos = source.readLong();
         presentationDeadlineNanos = source.readLong();
         state = source.readInt();
+        committedState = source.readInt();
         ownerUid = source.readInt();
         ownerPackageName = source.readString8();
         uniqueId = source.readString8();
@@ -538,6 +547,7 @@
         dest.writeLong(appVsyncOffsetNanos);
         dest.writeLong(presentationDeadlineNanos);
         dest.writeInt(state);
+        dest.writeInt(committedState);
         dest.writeInt(ownerUid);
         dest.writeString8(ownerPackageName);
         dest.writeString8(uniqueId);
@@ -761,6 +771,8 @@
         sb.append(rotation);
         sb.append(", state ");
         sb.append(Display.stateToString(state));
+        sb.append(", committedState ");
+        sb.append(Display.stateToString(committedState));
 
         if (Process.myUid() != Process.SYSTEM_UID) {
             sb.append("}");
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 61098d6..3247bb7 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -49,6 +51,12 @@
  * @hide
  */
 public class HandwritingInitiator {
+    /** The amount of extra space added to handwriting in dip. */
+    private static final int HANDWRITING_AREA_PADDING_DIP = 20;
+
+    /** The amount of extra space added to handwriting in px. */
+    private final float mHandwritingAreaPaddingPx;
+
     /**
      * The touchSlop from {@link ViewConfiguration} used to decide whether a pointer is considered
      * moving or stationary.
@@ -88,9 +96,13 @@
 
     @VisibleForTesting
     public HandwritingInitiator(@NonNull ViewConfiguration viewConfiguration,
-            @NonNull InputMethodManager inputMethodManager) {
+            @NonNull InputMethodManager inputMethodManager, DisplayMetrics displayMetrics) {
         mTouchSlop = viewConfiguration.getScaledTouchSlop();
         mHandwritingTimeoutInMillis = ViewConfiguration.getLongPressTimeout();
+        mHandwritingAreaPaddingPx = TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP,
+                HANDWRITING_AREA_PADDING_DIP,
+                displayMetrics);
         mImm = inputMethodManager;
     }
 
@@ -250,7 +262,8 @@
         }
 
         final Rect handwritingArea = getViewHandwritingArea(connectedView);
-        if (contains(handwritingArea, mState.mStylusDownX, mState.mStylusDownY)) {
+        if (containsInExtendedHandwritingArea(handwritingArea,
+                mState.mStylusDownX, mState.mStylusDownY)) {
             startHandwriting(connectedView);
         } else {
             reset();
@@ -281,14 +294,21 @@
      */
     @Nullable
     private View findBestCandidateView(float x, float y) {
+        float minDistance = Float.MAX_VALUE;
+        View bestCandidate = null;
+
         // If the connectedView is not null and do not set any handwriting area, it will check
         // whether the connectedView's boundary contains the initial stylus position. If true,
         // directly return the connectedView.
         final View connectedView = getConnectedView();
         if (connectedView != null && connectedView.isAutoHandwritingEnabled()) {
-            final Rect handwritingArea = getViewHandwritingArea(connectedView);
-            if (contains(handwritingArea, x, y)) {
-                return connectedView;
+            Rect handwritingArea = getViewHandwritingArea(connectedView);
+            if (containsInExtendedHandwritingArea(handwritingArea, x, y)) {
+                final float distance = distance(handwritingArea, x, y);
+                if (distance == 0f) return connectedView;
+
+                bestCandidate = connectedView;
+                minDistance = distance;
             }
         }
 
@@ -297,18 +317,78 @@
                 mHandwritingAreasTracker.computeViewInfos();
         for (HandwritableViewInfo viewInfo : handwritableViewInfos) {
             final View view = viewInfo.getView();
-            if (!view.isAutoHandwritingEnabled()) continue;
-            if (contains(viewInfo.getHandwritingArea(), x, y)) {
-                return viewInfo.getView();
+            final Rect handwritingArea = viewInfo.getHandwritingArea();
+            if (!containsInExtendedHandwritingArea(handwritingArea, x, y)) continue;
+
+            final float distance = distance(handwritingArea, x, y);
+
+            if (distance == 0f) return view;
+            if (distance < minDistance) {
+                minDistance = distance;
+                bestCandidate = view;
             }
         }
-        return null;
+        return bestCandidate;
+    }
+
+    /**
+     *  Return the square of the distance from point (x, y) to the given rect, which is mainly used
+     *  for comparison. The distance is defined to be: the shortest distance between (x, y) to any
+     *  point on rect. When (x, y) is contained by the rect, return 0f.
+     */
+    private static float distance(@NonNull Rect rect, float x, float y) {
+        if (contains(rect, x, y, 0f, 0f, 0f, 0f)) {
+            return 0f;
+        }
+
+        /* The distance between point (x, y) and rect, there are 2 basic cases:
+         * a) The distance is the distance from (x, y) to the closest corner on rect.
+         *                    o |     |
+         *         ---+-----+---
+         *            |     |
+         *         ---+-----+---
+         *            |     |
+         * b) The distance is the distance from (x, y) to the closest edge on rect.
+         *                      |  o  |
+         *         ---+-----+---
+         *            |     |
+         *         ---+-----+---
+         *            |     |
+         * We define xDistance as following(similar for yDistance):
+         *   If x is in [left, right) 0, else min(abs(x - left), abs(x - y))
+         * For case a, sqrt(xDistance^2 + yDistance^2) is the final distance.
+         * For case b, distance should be yDistance, which is also equal to
+         * sqrt(xDistance^2 + yDistance^2) because xDistance is 0.
+         */
+        final float xDistance;
+        if (x >= rect.left && x < rect.right) {
+            xDistance = 0f;
+        } else if (x < rect.left) {
+            xDistance = rect.left - x;
+        } else {
+            xDistance = x - rect.right;
+        }
+
+        final float yDistance;
+        if (y >= rect.top && y < rect.bottom) {
+            yDistance = 0f;
+        } else if (y < rect.top) {
+            yDistance = rect.top - y;
+        } else {
+            yDistance = y - rect.bottom;
+        }
+        // We can omit sqrt here because we only need the distance for comparison.
+        return xDistance * xDistance + yDistance * yDistance;
     }
 
     /**
      * Return the handwriting area of the given view, represented in the window's coordinate.
      * If the view didn't set any handwriting area, it will return the view's boundary.
      * It will return null if the view or its handwriting area is not visible.
+     *
+     * The handwriting area is clipped to its visible part.
+     * Notice that the returned rectangle is the view's original handwriting area without the
+     * view's handwriting area extends.
      */
     @Nullable
     private static Rect getViewHandwritingArea(@NonNull View view) {
@@ -329,11 +409,24 @@
     }
 
     /**
-     * Return true if the (x, y) is inside by the given {@link Rect}.
+     * Return true if the (x, y) is inside by the given {@link Rect} extended by the View's
+     * handwriting extends settings.
      */
-    private boolean contains(@Nullable Rect rect, float x, float y) {
-        if (rect == null) return false;
-        return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
+    private boolean containsInExtendedHandwritingArea(@Nullable Rect handwritingArea,
+            float x, float y) {
+        if (handwritingArea == null) return false;
+        return contains(handwritingArea, x, y, mHandwritingAreaPaddingPx, mHandwritingAreaPaddingPx,
+                mHandwritingAreaPaddingPx, mHandwritingAreaPaddingPx);
+    }
+
+    /**
+     * Return true if the (x, y) is inside by the given {@link Rect} extended by the given
+     * extendLeft, extendTop, extendRight and extendBottom.
+     */
+    private static boolean contains(@NonNull Rect rect, float x, float y,
+            float extendLeft, float extendTop, float extendRight, float extendBottom) {
+        return x >= rect.left - extendLeft && x < rect.right  + extendRight
+                && y >= rect.top - extendTop && y < rect.bottom + extendBottom;
     }
 
     private boolean largerThanTouchSlop(float x1, float y1, float x2, float y2) {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 7d56039..addbab0 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -517,6 +518,7 @@
      * @param id The device id.
      * @return The input device or null if not found.
      */
+    @Nullable
     public static InputDevice getDevice(int id) {
         return InputManager.getInstance().getInputDevice(id);
     }
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 2b79bbf..f838d7f 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -17,9 +17,9 @@
 package android.view;
 
 import android.annotation.IntDef;
+import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
-import android.sysprop.InputProperties;
 import android.util.ArrayMap;
 import android.util.Pools.SynchronizedPool;
 
@@ -278,8 +278,10 @@
     private VelocityTracker(@VelocityTrackerStrategy int strategy) {
         // If user has not selected a specific strategy
         if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) {
+            final String strategyProperty = ViewConfiguration.get(
+                    ActivityThread.currentActivityThread().getApplication())
+                    .getVelocityTrackerStrategy();
             // Check if user specified strategy by overriding system property.
-            String strategyProperty = InputProperties.velocitytracker_strategy().orElse(null);
             if (strategyProperty == null || strategyProperty.isEmpty()) {
                 mStrategy = strategy;
             } else {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8b9a86b9..88449f5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -31408,6 +31408,10 @@
      * {@link android.view.inputmethod.InputMethodManager#startStylusHandwriting(View)} when there
      * is stylus movement detected.
      *
+     * Note that this attribute has no effect on the View's children. For example, if a
+     * {@link ViewGroup} disables auto handwriting but its children set auto handwriting to true,
+     * auto handwriting will still work for the children, and vice versa.
+     *
      * @see #onCreateInputConnection(EditorInfo)
      * @see android.view.inputmethod.InputMethodManager#startStylusHandwriting(View)
      * @param enabled whether auto handwriting initiation is enabled for this view.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index ebc409e..30ae9c8 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -27,6 +27,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.hardware.input.InputManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -348,6 +349,7 @@
     private final int mSmartSelectionInitializedTimeout;
     private final int mSmartSelectionInitializingTimeout;
     private final int mPreferKeepClearForFocusDelay;
+    private final String mVelocityTrackerStrategy;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768915)
     private boolean sHasPermanentMenuKey;
@@ -394,6 +396,7 @@
         mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND;
         mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND;
         mPreferKeepClearForFocusDelay = -1;
+        mVelocityTrackerStrategy = InputManager.getInstance().getVelocityTrackerStrategy();
     }
 
     /**
@@ -510,6 +513,16 @@
                 com.android.internal.R.integer.config_smartSelectionInitializingTimeoutMillis);
         mPreferKeepClearForFocusDelay = res.getInteger(
                 com.android.internal.R.integer.config_preferKeepClearForFocusDelayMillis);
+
+        mVelocityTrackerStrategy = InputManager.getInstance().getVelocityTrackerStrategy();
+    }
+
+    /**
+     * Get the current VelocityTracker strategy
+     * @hide
+     */
+    public String getVelocityTrackerStrategy() {
+        return mVelocityTrackerStrategy;
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a3d0bf7..f310e7f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -890,8 +890,10 @@
         mChoreographer = Choreographer.getInstance();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
-        mHandwritingInitiator = new HandwritingInitiator(mViewConfiguration,
-                mContext.getSystemService(InputMethodManager.class));
+        mHandwritingInitiator = new HandwritingInitiator(
+                mViewConfiguration,
+                mContext.getSystemService(InputMethodManager.class),
+                context.getResources().getDisplayMetrics());
 
         String processorOverrideName = context.getResources().getString(
                                     R.string.config_inputEventCompatProcessorOverrideClassName);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 0008aa6..af8a017 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3063,6 +3063,9 @@
             int spanToReplaceStart = spannable.getSpanStart(span);
             int spanToReplaceEnd = spannable.getSpanEnd(span);
             int spanToReplaceFlags = spannable.getSpanFlags(span);
+            if (spanToReplaceStart < 0) {
+                continue;
+            }
             spannable.removeSpan(span);
             ClickableSpan replacementSpan = (span instanceof URLSpan)
                     ? new AccessibilityURLSpan((URLSpan) span)
@@ -3100,6 +3103,9 @@
             int spanToReplaceStart = spannable.getSpanStart(span);
             int spanToReplaceEnd = spannable.getSpanEnd(span);
             int spanToReplaceFlags = spannable.getSpanFlags(span);
+            if (spanToReplaceStart < 0) {
+                continue;
+            }
             spannable.removeSpan(span);
             ReplacementSpan replacementSpan = new AccessibilityReplacementSpan(replacementText);
             spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index b05b791..92cf745 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -27,6 +27,7 @@
 import static android.view.autofill.Helper.toList;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -47,17 +48,22 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.metrics.LogMaker;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.DeviceConfig;
 import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
 import android.service.autofill.FillEventHistory;
+import android.service.autofill.IFillCallback;
 import android.service.autofill.UserData;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -78,6 +84,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 import android.widget.TextView;
@@ -102,6 +109,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 import sun.misc.Cleaner;
 
@@ -170,6 +178,12 @@
  * shows an autofill save UI if the value of savable views have changed. If the user selects the
  * option to Save, the current value of the views is then sent to the autofill service.
  *
+ * <p>There is another choice for the application to provide it's datasets to the Autofill framework
+ * by setting an {@link AutofillRequestCallback} through
+ * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
+ * its callback instead of the default {@link AutofillService}. See
+ * {@link AutofillRequestCallback} for more details.
+ *
  * <h3 id="additional-notes">Additional notes</h3>
  *
  * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -295,6 +309,7 @@
     /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
     /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
     /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
+    /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
 
     // NOTE: flag below is used by the session start receiver only, hence it can have values above
     /** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -624,6 +639,11 @@
     @GuardedBy("mLock")
     private boolean mEnabledForAugmentedAutofillOnly;
 
+    @GuardedBy("mLock")
+    @Nullable private AutofillRequestCallback mAutofillRequestCallback;
+    @GuardedBy("mLock")
+    @Nullable private Executor mRequestCallbackExecutor;
+
     /**
      * Indicates whether there are any fields that need to do a fill request
      * after the activity starts.
@@ -1952,6 +1972,32 @@
         return new AutofillId(parent.getAutofillViewId(), virtualId);
     }
 
+    /**
+     * Sets the client's suggestions callback for autofill.
+     *
+     * @see AutofillRequestCallback
+     *
+     * @param executor specifies the thread upon which the callbacks will be invoked.
+     * @param callback which handles autofill request to provide client's suggestions.
+     */
+    public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull AutofillRequestCallback callback) {
+        synchronized (mLock) {
+            mRequestCallbackExecutor = executor;
+            mAutofillRequestCallback = callback;
+        }
+    }
+
+    /**
+     * clears the client's suggestions callback for autofill.
+     */
+    public void clearAutofillRequestCallback() {
+        synchronized (mLock) {
+            mRequestCallbackExecutor = null;
+            mAutofillRequestCallback = null;
+        }
+    }
+
     @GuardedBy("mLock")
     private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
             @NonNull AutofillValue value, int flags) {
@@ -2012,6 +2058,13 @@
                 }
             }
 
+            if (mAutofillRequestCallback != null) {
+                if (sDebug) {
+                    Log.d(TAG, "startSession with the client suggestions provider");
+                }
+                flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
+            }
+
             mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                     mCallback != null, flags, clientActivity,
@@ -2365,6 +2418,28 @@
         }
     }
 
+    private void onFillRequest(InlineSuggestionsRequest request,
+            CancellationSignal cancellationSignal, FillCallback callback) {
+        final AutofillRequestCallback autofillRequestCallback;
+        final Executor executor;
+        synchronized (mLock) {
+            autofillRequestCallback = mAutofillRequestCallback;
+            executor = mRequestCallbackExecutor;
+        }
+        if (autofillRequestCallback != null && executor != null) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                executor.execute(() ->
+                        autofillRequestCallback.onFillRequest(
+                                request, cancellationSignal, callback));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        } else {
+            callback.onSuccess(null);
+        }
+    }
+
     /** @hide */
     public static final int SET_STATE_FLAG_ENABLED = 0x01;
     /** @hide */
@@ -3852,6 +3927,23 @@
             }
         }
 
+        @Override
+        public void requestFillFromClient(int id, InlineSuggestionsRequest request,
+                IFillCallback callback) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                ICancellationSignal transport = CancellationSignal.createTransport();
+                try {
+                    callback.onCancellable(transport);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error requesting a cancellation", e);
+                }
+
+                afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
+                        new FillCallback(callback, id));
+            }
+        }
+
         public void notifyFillDialogTriggerIds(List<AutofillId> ids) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java
new file mode 100644
index 0000000..e632a58
--- /dev/null
+++ b/core/java/android/view/autofill/AutofillRequestCallback.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CancellationSignal;
+import android.service.autofill.FillCallback;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+/**
+ * <p>This class is used to provide some input suggestions to the Autofill framework.
+ *
+ * <P>When the user is requested to input something, Autofill will try to query input suggestions
+ * for the user choosing. If the application want to provide some internal input suggestions,
+ * implements this callback and register via
+ * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
+ * AutofillRequestCallback)}. Autofill will callback the
+ * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
+ * input suggestions.
+ *
+ * <P>To make sure the callback to take effect, must register before the autofill session starts.
+ * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
+ * session, and then the callback will be used at the next restarted session.
+ *
+ * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
+ * {@link AutofillId}s from its view structure. Below is an example:
+ * <pre class="prettyprint">
+ * AutofillId usernameId = findViewById(R.id.username).getAutofillId();
+ * AutofillId passwordId = findViewById(R.id.password).getAutofillId();
+ * </pre>
+ * To learn more about creating a {@link android.service.autofill.FillResponse}, read
+ * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
+ *
+ * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
+ * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
+ * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
+ * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
+ * client would like to keep no suggestions for the field, respond with an empty
+ * {@link android.service.autofill.FillResponse} which has no dataset.
+ *
+ * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
+ * the keyboard may choose to block your app from the inline strip.
+ */
+public interface AutofillRequestCallback {
+    /**
+     * Called by the Android system to decide if a screen can be autofilled by the callback.
+     *
+     * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
+     *     currently inline suggestions are supported and can be displayed.
+     * @param cancellationSignal signal for observing cancellation requests. The system will use
+     *     this to notify you that the fill result is no longer needed and you should stop
+     *     handling this fill request in order to save resources.
+     * @param callback object used to notify the result of the request.
+     */
+    void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 51afe4c..2e5967c 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -24,9 +24,11 @@
 import android.content.IntentSender;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.service.autofill.IFillCallback;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutofillWindowPresenter;
+import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.KeyEvent;
 
 import com.android.internal.os.IResultReceiver;
@@ -142,6 +144,12 @@
    void requestShowSoftInput(in AutofillId id);
 
     /**
+     * Requests to determine if a screen can be autofilled by the client app.
+     */
+    void requestFillFromClient(int id, in InlineSuggestionsRequest request,
+            in IFillCallback callback);
+
+    /**
      * Notifies autofill ids that require to show the fill dialog.
      */
     void notifyFillDialogTriggerIds(in List<AutofillId> ids);
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index c78b810..70279cc 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -111,6 +111,22 @@
     private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
 
     /**
+     * Whether the IME supports inline suggestions from the default Autofill service that
+     * provides the input view.
+     *
+     * Note: The default value is {@code true}.
+     */
+    private boolean mServiceSupported;
+
+    /**
+     * Whether the IME supports inline suggestions from the application that provides the
+     * input view.
+     *
+     * Note: The default value is {@code true}.
+     */
+    private boolean mClientSupported;
+
+    /**
      * @hide
      * @see {@link #mHostInputToken}.
      */
@@ -204,6 +220,14 @@
         return Bundle.EMPTY;
     }
 
+    private static boolean defaultServiceSupported() {
+        return true;
+    }
+
+    private static boolean defaultClientSupported() {
+        return true;
+    }
+
     /** @hide */
     abstract static class BaseBuilder {
         abstract Builder setInlinePresentationSpecs(
@@ -216,15 +240,25 @@
         abstract Builder setHostDisplayId(int value);
     }
 
+    /** @hide */
+    public boolean isServiceSupported() {
+        return mServiceSupported;
+    }
+
+    /** @hide */
+    public boolean isClientSupported() {
+        return mClientSupported;
+    }
 
 
-    // Code below generated by codegen v1.0.23.
+
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -240,7 +274,9 @@
             @NonNull Bundle extras,
             @Nullable IBinder hostInputToken,
             int hostDisplayId,
-            @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) {
+            @Nullable InlinePresentationSpec inlineTooltipPresentationSpec,
+            boolean serviceSupported,
+            boolean clientSupported) {
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mInlinePresentationSpecs = inlinePresentationSpecs;
         com.android.internal.util.AnnotationValidations.validate(
@@ -257,6 +293,8 @@
         this.mHostInputToken = hostInputToken;
         this.mHostDisplayId = hostDisplayId;
         this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+        this.mServiceSupported = serviceSupported;
+        this.mClientSupported = clientSupported;
 
         onConstructed();
     }
@@ -340,7 +378,9 @@
     }
 
     /**
-     * Specifies the UI specification for the inline suggestion tooltip in the response.
+     * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+     *
+     * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)
      */
     @DataClass.Generated.Member
     public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() {
@@ -361,7 +401,9 @@
                 "extras = " + mExtras + ", " +
                 "hostInputToken = " + mHostInputToken + ", " +
                 "hostDisplayId = " + mHostDisplayId + ", " +
-                "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec +
+                "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " +
+                "serviceSupported = " + mServiceSupported + ", " +
+                "clientSupported = " + mClientSupported +
         " }";
     }
 
@@ -385,7 +427,9 @@
                 && extrasEquals(that.mExtras)
                 && java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
                 && mHostDisplayId == that.mHostDisplayId
-                && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec);
+                && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec)
+                && mServiceSupported == that.mServiceSupported
+                && mClientSupported == that.mClientSupported;
     }
 
     @Override
@@ -403,6 +447,8 @@
         _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
         _hash = 31 * _hash + mHostDisplayId;
         _hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec);
+        _hash = 31 * _hash + Boolean.hashCode(mServiceSupported);
+        _hash = 31 * _hash + Boolean.hashCode(mClientSupported);
         return _hash;
     }
 
@@ -413,6 +459,8 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         int flg = 0;
+        if (mServiceSupported) flg |= 0x100;
+        if (mClientSupported) flg |= 0x200;
         if (mHostInputToken != null) flg |= 0x20;
         if (mInlineTooltipPresentationSpec != null) flg |= 0x80;
         dest.writeInt(flg);
@@ -438,6 +486,8 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         int flg = in.readInt();
+        boolean serviceSupported = (flg & 0x100) != 0;
+        boolean clientSupported = (flg & 0x200) != 0;
         int maxSuggestionCount = in.readInt();
         List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
         in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader(), android.widget.inline.InlinePresentationSpec.class);
@@ -464,6 +514,8 @@
         this.mHostInputToken = hostInputToken;
         this.mHostDisplayId = hostDisplayId;
         this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+        this.mServiceSupported = serviceSupported;
+        this.mClientSupported = clientSupported;
 
         onConstructed();
     }
@@ -497,6 +549,8 @@
         private @Nullable IBinder mHostInputToken;
         private int mHostDisplayId;
         private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
+        private boolean mServiceSupported;
+        private boolean mClientSupported;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -629,7 +683,9 @@
         }
 
         /**
-         * Specifies the UI specification for the inline suggestion tooltip in the response.
+         * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+         *
+         * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s
          */
         @DataClass.Generated.Member
         public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) {
@@ -639,10 +695,38 @@
             return this;
         }
 
+        /**
+         * Whether the IME supports inline suggestions from the default Autofill service that
+         * provides the input view.
+         *
+         * Note: The default value is {@code true}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setServiceSupported(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x100;
+            mServiceSupported = value;
+            return this;
+        }
+
+        /**
+         * Whether the IME supports inline suggestions from the application that provides the
+         * input view.
+         *
+         * Note: The default value is {@code true}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setClientSupported(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x200;
+            mClientSupported = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull InlineSuggestionsRequest build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x100; // Mark builder used
+            mBuilderFieldsSet |= 0x400; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -665,6 +749,12 @@
             if ((mBuilderFieldsSet & 0x80) == 0) {
                 mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec();
             }
+            if ((mBuilderFieldsSet & 0x100) == 0) {
+                mServiceSupported = defaultServiceSupported();
+            }
+            if ((mBuilderFieldsSet & 0x200) == 0) {
+                mClientSupported = defaultClientSupported();
+            }
             InlineSuggestionsRequest o = new InlineSuggestionsRequest(
                     mMaxSuggestionCount,
                     mInlinePresentationSpecs,
@@ -673,12 +763,14 @@
                     mExtras,
                     mHostInputToken,
                     mHostDisplayId,
-                    mInlineTooltipPresentationSpec);
+                    mInlineTooltipPresentationSpec,
+                    mServiceSupported,
+                    mClientSupported);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x100) != 0) {
+            if ((mBuilderFieldsSet & 0x400) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -686,10 +778,10 @@
     }
 
     @DataClass.Generated(
-            time = 1621415989607L,
-            codegenVersion = "1.0.23",
+            time = 1615798784918L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\npublic  void filterContentTypes()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate  boolean mServiceSupported\nprivate  boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\npublic  void filterContentTypes()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static  boolean defaultServiceSupported()\nprivate static  boolean defaultClientSupported()\npublic  boolean isServiceSupported()\npublic  boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 805f8e7..52d88d6 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2918,7 +2918,8 @@
      */
     @Deprecated
     public void hideSoftInputFromInputMethod(IBinder token, int flags) {
-        InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags);
+        InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(
+                flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION);
     }
 
     /**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 231ae08..184e7bc 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4363,8 +4363,35 @@
 
                 final int delta = Math.round(axisValue * mVerticalScrollFactor);
                 if (delta != 0) {
+                    // If we're moving down, we want the top item. If we're moving up, bottom item.
+                    final int motionIndex = delta > 0 ? 0 : getChildCount() - 1;
+
+                    int motionViewPrevTop = 0;
+                    View motionView = this.getChildAt(motionIndex);
+                    if (motionView != null) {
+                        motionViewPrevTop = motionView.getTop();
+                    }
+
+                    final int overscrollMode = getOverScrollMode();
+
                     if (!trackMotionScroll(delta, delta)) {
                         return true;
+                    } else if (!event.isFromSource(InputDevice.SOURCE_MOUSE) && motionView != null
+                            && (overscrollMode == OVER_SCROLL_ALWAYS
+                            || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS
+                            && !contentFits()))) {
+                        int motionViewRealTop = motionView.getTop();
+                        float overscroll = (delta - (motionViewRealTop - motionViewPrevTop))
+                                / ((float) getHeight());
+                        if (delta > 0) {
+                            mEdgeGlowTop.onPullDistance(overscroll, 0.5f);
+                            mEdgeGlowTop.onRelease();
+                        } else {
+                            mEdgeGlowBottom.onPullDistance(-overscroll, 0.5f);
+                            mEdgeGlowBottom.onRelease();
+                        }
+                        invalidate();
+                        return true;
                     }
                 }
                 break;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 018cba7..2dbfd7e 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -872,15 +872,39 @@
                         final int range = getScrollRange();
                         int oldScrollX = mScrollX;
                         int newScrollX = oldScrollX + delta;
+
+                        final int overscrollMode = getOverScrollMode();
+                        boolean canOverscroll = !event.isFromSource(InputDevice.SOURCE_MOUSE)
+                                && (overscrollMode == OVER_SCROLL_ALWAYS
+                                || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0));
+                        boolean absorbed = false;
+
                         if (newScrollX < 0) {
+                            if (canOverscroll) {
+                                mEdgeGlowLeft.onPullDistance(-(float) newScrollX / getWidth(),
+                                        0.5f);
+                                mEdgeGlowLeft.onRelease();
+                                invalidate();
+                                absorbed = true;
+                            }
                             newScrollX = 0;
                         } else if (newScrollX > range) {
+                            if (canOverscroll) {
+                                mEdgeGlowRight.onPullDistance(
+                                        (float) (newScrollX - range) / getWidth(), 0.5f);
+                                mEdgeGlowRight.onRelease();
+                                invalidate();
+                                absorbed = true;
+                            }
                             newScrollX = range;
                         }
                         if (newScrollX != oldScrollX) {
                             super.scrollTo(newScrollX, mScrollY);
                             return true;
                         }
+                        if (absorbed) {
+                            return true;
+                        }
                     }
                 }
             }
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 8e293f4..61a7599 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -52,9 +52,9 @@
 import com.android.internal.widget.IRemoteViewsFactory;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.concurrent.Executor;
 
 /**
@@ -424,17 +424,17 @@
      * adapter that have not yet had their RemoteViews loaded.
      */
     private class RemoteViewsFrameLayoutRefSet
-            extends SparseArray<LinkedList<RemoteViewsFrameLayout>> {
+            extends SparseArray<ArrayList<RemoteViewsFrameLayout>> {
 
         /**
          * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
          */
         public void add(int position, RemoteViewsFrameLayout layout) {
-            LinkedList<RemoteViewsFrameLayout> refs = get(position);
+            ArrayList<RemoteViewsFrameLayout> refs = get(position);
 
             // Create the list if necessary
             if (refs == null) {
-                refs = new LinkedList<>();
+                refs = new ArrayList<>();
                 put(position, refs);
             }
 
@@ -451,7 +451,7 @@
             if (view == null) return;
 
             // Remove this set from the original mapping
-            final LinkedList<RemoteViewsFrameLayout> refs = removeReturnOld(position);
+            final ArrayList<RemoteViewsFrameLayout> refs = removeReturnOld(position);
             if (refs != null) {
                 // Notify all the references for that position of the newly loaded RemoteViews
                 for (final RemoteViewsFrameLayout ref : refs) {
@@ -467,7 +467,7 @@
             if (rvfl.cacheIndex < 0) {
                 return;
             }
-            final LinkedList<RemoteViewsFrameLayout> refs = get(rvfl.cacheIndex);
+            final ArrayList<RemoteViewsFrameLayout> refs = get(rvfl.cacheIndex);
             if (refs != null) {
                 refs.remove(rvfl);
             }
@@ -933,12 +933,8 @@
 
             Runnable r = () -> {
                 synchronized (sCachedRemoteViewsCaches) {
-                    if (sCachedRemoteViewsCaches.containsKey(key)) {
-                        sCachedRemoteViewsCaches.remove(key);
-                    }
-                    if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
-                        sRemoteViewsCacheRemoveRunnables.remove(key);
-                    }
+                    sCachedRemoteViewsCaches.remove(key);
+                    sRemoteViewsCacheRemoveRunnables.remove(key);
                 }
             };
             sRemoteViewsCacheRemoveRunnables.put(key, r);
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 15cd17b..2acd50c 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -939,15 +939,38 @@
                     final int range = getScrollRange();
                     int oldScrollY = mScrollY;
                     int newScrollY = oldScrollY - delta;
+
+                    final int overscrollMode = getOverScrollMode();
+                    boolean canOverscroll = !event.isFromSource(InputDevice.SOURCE_MOUSE)
+                            && (overscrollMode == OVER_SCROLL_ALWAYS
+                            || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0));
+                    boolean absorbed = false;
+
                     if (newScrollY < 0) {
+                        if (canOverscroll) {
+                            mEdgeGlowTop.onPullDistance(-(float) newScrollY / getHeight(), 0.5f);
+                            mEdgeGlowTop.onRelease();
+                            invalidate();
+                            absorbed = true;
+                        }
                         newScrollY = 0;
                     } else if (newScrollY > range) {
+                        if (canOverscroll) {
+                            mEdgeGlowBottom.onPullDistance(
+                                    (float) (newScrollY - range) / getHeight(), 0.5f);
+                            mEdgeGlowBottom.onRelease();
+                            invalidate();
+                            absorbed = true;
+                        }
                         newScrollY = range;
                     }
                     if (newScrollY != oldScrollY) {
                         super.scrollTo(mScrollX, newScrollY);
                         return true;
                     }
+                    if (absorbed) {
+                        return true;
+                    }
                 }
                 break;
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 54c0d7c..9e1bc8f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -144,6 +144,7 @@
 import android.text.util.Linkify;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.FeatureFlagUtils;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -791,6 +792,11 @@
     private int mLineBreakStyle = DEFAULT_LINE_BREAK_STYLE;
     private int mLineBreakWordStyle = DEFAULT_LINE_BREAK_WORD_STYLE;
 
+    // The auto option for LINE_BREAK_WORD_STYLE_PHRASE may not be applied in recycled view due to
+    // one-way flag flipping. This is a tentative limitation during experiment and will not have the
+    // issue once this is finalized to LINE_BREAK_WORD_STYLE_PHRASE_AUTO option.
+    private boolean mUserSpeficiedLineBreakwordStyle = false;
+
     // This is used to reflect the current user preference for changing font weight and making text
     // more bold.
     private int mFontWeightAdjustment;
@@ -1462,6 +1468,9 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_lineBreakWordStyle:
+                    if (a.hasValue(attr)) {
+                        mUserSpeficiedLineBreakwordStyle = true;
+                    }
                     mLineBreakWordStyle = a.getInt(attr,
                             LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE);
                     break;
@@ -4209,6 +4218,7 @@
                     break;
                 case com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle:
                     attributes.mHasLineBreakWordStyle = true;
+                    mUserSpeficiedLineBreakwordStyle = true;
                     attributes.mLineBreakWordStyle =
                             appearance.getInt(attr, attributes.mLineBreakWordStyle);
                     break;
@@ -4910,6 +4920,7 @@
      * @param lineBreakWordStyle the line break word style for the tet
      */
     public void setLineBreakWordStyle(@LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
+        mUserSpeficiedLineBreakwordStyle = true;
         if (mLineBreakWordStyle != lineBreakWordStyle) {
             mLineBreakWordStyle = lineBreakWordStyle;
             if (mLayout != null) {
@@ -4945,8 +4956,12 @@
      * @see PrecomputedText
      */
     public @NonNull PrecomputedText.Params getTextMetricsParams() {
+        final boolean autoPhraseBreaking =
+                !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
+                        FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
         return new PrecomputedText.Params(new TextPaint(mTextPaint),
-                LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle),
+                LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle,
+                        autoPhraseBreaking),
                 getTextDirectionHeuristic(),
                 mBreakStrategy, mHyphenationFrequency);
     }
@@ -4966,6 +4981,7 @@
         LineBreakConfig lineBreakConfig = params.getLineBreakConfig();
         mLineBreakStyle = lineBreakConfig.getLineBreakStyle();
         mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle();
+        mUserSpeficiedLineBreakwordStyle = true;
         if (mLayout != null) {
             nullLayouts();
             requestLayout();
@@ -6502,10 +6518,13 @@
             if (mTextDir == null) {
                 mTextDir = getTextDirectionHeuristic();
             }
+            final boolean autoPhraseBreaking =
+                    !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
+                            FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
             final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
                     precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy,
                             mHyphenationFrequency, LineBreakConfig.getLineBreakConfig(
-                                    mLineBreakStyle, mLineBreakWordStyle));
+                                    mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
             switch (checkResult) {
                 case PrecomputedText.Params.UNUSABLE:
                     throw new IllegalArgumentException(
@@ -9391,6 +9410,9 @@
             }
             // TODO: code duplication with makeSingleLayout()
             if (mHintLayout == null) {
+                final boolean autoPhraseBreaking =
+                        !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
+                                FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
                 StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
                         mHint.length(), mTextPaint, hintWidth)
                         .setAlignment(alignment)
@@ -9403,7 +9425,7 @@
                         .setJustificationMode(mJustificationMode)
                         .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
                         .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
-                                mLineBreakStyle, mLineBreakWordStyle));
+                                mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
                 if (shouldEllipsize) {
                     builder.setEllipsize(mEllipsize)
                             .setEllipsizedWidth(ellipsisWidth);
@@ -9507,6 +9529,9 @@
             }
         }
         if (result == null) {
+            final boolean autoPhraseBreaking =
+                    !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
+                            FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
             StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
                     0, mTransformed.length(), mTextPaint, wantWidth)
                     .setAlignment(alignment)
@@ -9519,7 +9544,7 @@
                     .setJustificationMode(mJustificationMode)
                     .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
                     .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
-                            mLineBreakStyle, mLineBreakWordStyle));
+                            mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
             if (shouldEllipsize) {
                 builder.setEllipsize(effectiveEllipsize)
                         .setEllipsizedWidth(ellipsisWidth);
@@ -9877,7 +9902,9 @@
 
         final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(
                 text, 0, text.length(),  mTempTextPaint, Math.round(availableSpace.right));
-
+        final boolean autoPhraseBreaking =
+                !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
+                        FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
         layoutBuilder.setAlignment(getLayoutAlignment())
                 .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
                 .setIncludePad(getIncludeFontPadding())
@@ -9888,7 +9915,7 @@
                 .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
                 .setTextDirection(getTextDirectionHeuristic())
                 .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
-                        mLineBreakStyle, mLineBreakWordStyle));
+                        mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
 
         final StaticLayout layout = layoutBuilder.build();
 
diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java
index 42fc7bd..9759540 100644
--- a/core/java/com/android/internal/app/AbstractResolverComparator.java
+++ b/core/java/com/android/internal/app/AbstractResolverComparator.java
@@ -228,12 +228,6 @@
      */
     abstract float getScore(ComponentName name);
 
-    /**
-     * Returns the list of top K component names which have highest
-     * {@link #getScore(ComponentName)}
-     */
-    abstract List<ComponentName> getTopComponentNames(int topK);
-
     /** Handles result message sent to mHandler. */
     abstract void handleResultMessage(Message message);
 
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index bc9eff0..b19ac2f 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -18,6 +18,7 @@
 
 import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH;
 
+import android.annotation.Nullable;
 import android.app.prediction.AppPredictor;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
@@ -33,12 +34,11 @@
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.concurrent.Executors;
-import java.util.stream.Collectors;
 
 /**
  * Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be
@@ -58,7 +58,9 @@
     private final String mReferrerPackage;
     // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall
     // back to using the ResolverRankerService.
+    // TODO: responsibility for this fallback behavior can live outside of the AppPrediction client.
     private ResolverRankerServiceResolverComparator mResolverRankerService;
+    private AppPredictionServiceComparatorModel mComparatorModel;
 
     AppPredictionServiceResolverComparator(
             Context context,
@@ -74,25 +76,12 @@
         mUser = user;
         mReferrerPackage = referrerPackage;
         setChooserActivityLogger(chooserActivityLogger);
+        mComparatorModel = buildUpdatedModel();
     }
 
     @Override
     int compare(ResolveInfo lhs, ResolveInfo rhs) {
-        if (mResolverRankerService != null) {
-            return mResolverRankerService.compare(lhs, rhs);
-        }
-        Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName,
-                lhs.activityInfo.name));
-        Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName,
-                rhs.activityInfo.name));
-        if (lhsRank == null && rhsRank == null) {
-            return 0;
-        } else if (lhsRank == null) {
-            return -1;
-        } else if (rhsRank == null) {
-            return 1;
-        }
-        return lhsRank - rhsRank;
+        return mComparatorModel.getComparator().compare(lhs, rhs);
     }
 
     @Override
@@ -121,6 +110,7 @@
                                         mContext, mIntent, mReferrerPackage,
                                         () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT),
                                         getChooserActivityLogger());
+                        mComparatorModel = buildUpdatedModel();
                         mResolverRankerService.compute(targets);
                     } else {
                         Log.i(TAG, "AppPredictionService response received");
@@ -163,6 +153,7 @@
             mTargetRanks.put(componentName, i);
             Log.i(TAG, "handleSortedAppTargets, sortedAppTargets #" + i + ": " + componentName);
         }
+        mComparatorModel = buildUpdatedModel();
     }
 
     private boolean checkAppTargetRankValid(List<AppTarget> sortedAppTargets) {
@@ -176,43 +167,12 @@
 
     @Override
     float getScore(ComponentName name) {
-        if (mResolverRankerService != null) {
-            return mResolverRankerService.getScore(name);
-        }
-        Integer rank = mTargetRanks.get(name);
-        if (rank == null) {
-            Log.w(TAG, "Score requested for unknown component. Did you call compute yet?");
-            return 0f;
-        }
-        int consecutiveSumOfRanks = (mTargetRanks.size() - 1) * (mTargetRanks.size()) / 2;
-        return 1.0f - (((float) rank) / consecutiveSumOfRanks);
-    }
-
-    @Override
-    List<ComponentName> getTopComponentNames(int topK) {
-        if (mResolverRankerService != null) {
-            return mResolverRankerService.getTopComponentNames(topK);
-        }
-        return mTargetRanks.entrySet().stream()
-                .sorted(Entry.comparingByValue())
-                .limit(topK)
-                .map(Entry::getKey)
-                .collect(Collectors.toList());
+        return mComparatorModel.getScore(name);
     }
 
     @Override
     void updateModel(ComponentName componentName) {
-        if (mResolverRankerService != null) {
-            mResolverRankerService.updateModel(componentName);
-            return;
-        }
-        mAppPredictor.notifyAppTargetEvent(
-                new AppTargetEvent.Builder(
-                    new AppTarget.Builder(
-                        new AppTargetId(componentName.toString()),
-                        componentName.getPackageName(), mUser)
-                        .setClassName(componentName.getClassName()).build(),
-                    ACTION_LAUNCH).build());
+        mComparatorModel.notifyOnTargetSelected(componentName);
     }
 
     @Override
@@ -220,6 +180,97 @@
         if (mResolverRankerService != null) {
             mResolverRankerService.destroy();
             mResolverRankerService = null;
+            mComparatorModel = buildUpdatedModel();
+        }
+    }
+
+    /**
+     * Re-construct an {@code AppPredictionServiceComparatorModel} to replace the current model
+     * instance (if any) using the up-to-date {@code AppPredictionServiceResolverComparator} ivar
+     * values.
+     *
+     * TODO: each time we replace the model instance, we're either updating the model to use
+     * adjusted data (which is appropriate), or we're providing a (late) value for one of our ivars
+     * that wasn't available the last time the model was updated. For those latter cases, we should
+     * just avoid creating the model altogether until we have all the prerequisites we'll need. Then
+     * we can probably simplify the logic in {@code AppPredictionServiceComparatorModel} since we
+     * won't need to handle edge cases when the model data isn't fully prepared.
+     * (In some cases, these kinds of "updates" might interleave -- e.g., we might have finished
+     * initializing the first time and now want to adjust some data, but still need to wait for
+     * changes to propagate to the other ivars before rebuilding the model.)
+     */
+    private AppPredictionServiceComparatorModel buildUpdatedModel() {
+        return new AppPredictionServiceComparatorModel(
+                mAppPredictor, mResolverRankerService, mUser, mTargetRanks);
+    }
+
+    // TODO: Finish separating behaviors of AbstractResolverComparator, then (probably) make this a
+    // standalone class once clients are written in terms of ResolverComparatorModel.
+    static class AppPredictionServiceComparatorModel implements ResolverComparatorModel {
+        private final AppPredictor mAppPredictor;
+        private final ResolverRankerServiceResolverComparator mResolverRankerService;
+        private final UserHandle mUser;
+        private final Map<ComponentName, Integer> mTargetRanks;  // Treat as immutable.
+
+        AppPredictionServiceComparatorModel(
+                AppPredictor appPredictor,
+                @Nullable ResolverRankerServiceResolverComparator resolverRankerService,
+                UserHandle user,
+                Map<ComponentName, Integer> targetRanks) {
+            mAppPredictor = appPredictor;
+            mResolverRankerService = resolverRankerService;
+            mUser = user;
+            mTargetRanks = targetRanks;
+        }
+
+        @Override
+        public Comparator<ResolveInfo> getComparator() {
+            return (lhs, rhs) -> {
+                if (mResolverRankerService != null) {
+                    return mResolverRankerService.compare(lhs, rhs);
+                }
+                Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName,
+                        lhs.activityInfo.name));
+                Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName,
+                        rhs.activityInfo.name));
+                if (lhsRank == null && rhsRank == null) {
+                    return 0;
+                } else if (lhsRank == null) {
+                    return -1;
+                } else if (rhsRank == null) {
+                    return 1;
+                }
+                return lhsRank - rhsRank;
+            };
+        }
+
+        @Override
+        public float getScore(ComponentName name) {
+            if (mResolverRankerService != null) {
+                return mResolverRankerService.getScore(name);
+            }
+            Integer rank = mTargetRanks.get(name);
+            if (rank == null) {
+                Log.w(TAG, "Score requested for unknown component. Did you call compute yet?");
+                return 0f;
+            }
+            int consecutiveSumOfRanks = (mTargetRanks.size() - 1) * (mTargetRanks.size()) / 2;
+            return 1.0f - (((float) rank) / consecutiveSumOfRanks);
+        }
+
+        @Override
+        public void notifyOnTargetSelected(ComponentName componentName) {
+            if (mResolverRankerService != null) {
+                mResolverRankerService.updateModel(componentName);
+                return;
+            }
+            mAppPredictor.notifyAppTargetEvent(
+                    new AppTargetEvent.Builder(
+                        new AppTarget.Builder(
+                            new AppTargetId(componentName.toString()),
+                            componentName.getPackageName(), mUser)
+                            .setClassName(componentName.getClassName()).build(),
+                        ACTION_LAUNCH).build());
         }
     }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index d4a8a16..595c963 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -194,9 +194,6 @@
     private static final String PLURALS_COUNT = "count";
     private static final String PLURALS_FILE_NAME = "file_name";
 
-    @VisibleForTesting
-    public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250;
-
     private boolean mIsAppPredictorComponentAvailable;
     private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
     private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache;
@@ -248,6 +245,13 @@
                     SystemUiDeviceConfigFlags.IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP,
                     DEFAULT_IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP);
 
+    private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 250;
+
+    @VisibleForTesting
+    int mListViewUpdateDelayMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
+            SystemUiDeviceConfigFlags.SHARESHEET_LIST_VIEW_UPDATE_DELAY,
+            DEFAULT_LIST_VIEW_UPDATE_DELAY_MS);
+
     private Bundle mReplacementExtras;
     private IntentSender mChosenComponentSender;
     private IntentSender mRefinementIntentSender;
@@ -2605,7 +2609,7 @@
         Message msg = Message.obtain();
         msg.what = ChooserHandler.LIST_VIEW_UPDATE_MESSAGE;
         msg.obj = userHandle;
-        mChooserHandler.sendMessageDelayed(msg, LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        mChooserHandler.sendMessageDelayed(msg, mListViewUpdateDelayMs);
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/ChooserUtil.java b/core/java/com/android/internal/app/ChooserUtil.java
deleted file mode 100644
index 3f8788c..0000000
--- a/core/java/com/android/internal/app/ChooserUtil.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2020 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.internal.app;
-
-import java.nio.charset.Charset;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Utility method for common computation operations for Share sheet.
- */
-public class ChooserUtil {
-
-    private static final Charset UTF_8 = Charset.forName("UTF-8");
-
-    /**
-     * Hashes the given input based on MD5 algorithm.
-     *
-     * @return a string representation of the hash computation.
-     */
-    public static String md5(String input) {
-        try {
-            MessageDigest md = MessageDigest.getInstance("MD5");
-            md.update(input.getBytes(UTF_8));
-            return convertBytesToHexString(md.digest());
-        } catch (NoSuchAlgorithmException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    /** Converts byte array input into an hex string. */
-    private static String convertBytesToHexString(byte[] input) {
-        char[] chars = new char[input.length * 2];
-        for (int i = 0; i < input.length; i++) {
-            byte b = input[i];
-            chars[i * 2] = Character.forDigit((b >> 4) & 0xF, 16 /* radix */);
-            chars[i * 2 + 1] = Character.forDigit(b & 0xF, 16 /* radix */);
-        }
-        return new String(chars);
-    }
-
-    private ChooserUtil() {}
-}
diff --git a/core/java/com/android/internal/app/ResolverComparatorModel.java b/core/java/com/android/internal/app/ResolverComparatorModel.java
new file mode 100644
index 0000000..3e8f64b
--- /dev/null
+++ b/core/java/com/android/internal/app/ResolverComparatorModel.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.ComponentName;
+import android.content.pm.ResolveInfo;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * A ranking model for resolver targets, providing ordering and (optionally) numerical scoring.
+ *
+ * As required by the {@link Comparator} contract, objects returned by {@code getComparator()} must
+ * apply a total ordering on its inputs consistent across all calls to {@code Comparator#compare()}.
+ * Other query methods and ranking feedback should refer to that same ordering, so implementors are
+ * generally advised to "lock in" an immutable snapshot of their model data when this object is
+ * initialized (preferring to replace the entire {@code ResolverComparatorModel} instance if the
+ * backing data needs to be updated in the future).
+ */
+interface ResolverComparatorModel {
+    /**
+     * Get a {@code Comparator} that can be used to sort {@code ResolveInfo} targets according to
+     * the model ranking.
+     */
+    Comparator<ResolveInfo> getComparator();
+
+    /**
+     * Get the numerical score, if any, that the model assigns to the component with the specified
+     * {@code name}. Scores range from zero to one, with one representing the highest possible
+     * likelihood that the user will select that component as the target. Implementations that don't
+     * assign numerical scores are <em>recommended</em> to return a value of 0 for all components.
+     */
+    float getScore(ComponentName name);
+
+    /**
+     * Notify the model that the user selected a target. (Models may log this information, use it as
+     * a feedback signal for their ranking, etc.) Because the data in this
+     * {@code ResolverComparatorModel} instance is immutable, clients will need to get an up-to-date
+     * instance in order to see any changes in the ranking that might result from this feedback.
+     */
+    void notifyOnTargetSelected(ComponentName componentName);
+}
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index ac9b2d8..988ecc78 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -155,14 +155,6 @@
         return mResolverListController.getScore(componentName);
     }
 
-    /**
-     * Returns the list of top K component names which have highest
-     * {@link #getScore(DisplayResolveInfo)}
-     */
-    public List<ComponentName> getTopComponentNames(int topK) {
-        return mResolverListController.getTopComponentNames(topK);
-    }
-
     public void updateModel(ComponentName componentName) {
         mResolverListController.updateModel(componentName);
     }
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 9a95e64..2757363 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -393,14 +393,6 @@
         return mResolverComparator.getScore(componentName);
     }
 
-    /**
-     * Returns the list of top K component names which have highest
-     * {@link #getScore(DisplayResolveInfo)}
-     */
-    public List<ComponentName> getTopComponentNames(int topK) {
-        return mResolverComparator.getTopComponentNames(topK);
-    }
-
     public void updateModel(ComponentName componentName) {
         mResolverComparator.updateModel(componentName);
     }
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index cb946c0..c5b21ac 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -43,12 +43,12 @@
 
 import java.text.Collator;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
 
 /**
  * Ranks and compares packages based on usage stats and uses the {@link ResolverRankerService}.
@@ -83,6 +83,7 @@
     private ResolverRankerServiceConnection mConnection;
     private Context mContext;
     private CountDownLatch mConnectSignal;
+    private ResolverRankerServiceComparatorModel mComparatorModel;
 
     public ResolverRankerServiceResolverComparator(Context context, Intent intent,
                 String referrerPackage, AfterCompute afterCompute,
@@ -99,6 +100,8 @@
         mRankerServiceName = new ComponentName(mContext, this.getClass());
         setCallBack(afterCompute);
         setChooserActivityLogger(chooserActivityLogger);
+
+        mComparatorModel = buildUpdatedModel();
     }
 
     @Override
@@ -125,6 +128,7 @@
             }
             if (isUpdated) {
                 mRankerServiceName = mResolvedRankerName;
+                mComparatorModel = buildUpdatedModel();
             }
         } else {
             Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
@@ -218,83 +222,25 @@
             }
         }
         predictSelectProbabilities(mTargets);
+
+        mComparatorModel = buildUpdatedModel();
     }
 
     @Override
     public int compare(ResolveInfo lhs, ResolveInfo rhs) {
-        if (mStats != null) {
-            final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName(
-                    lhs.activityInfo.packageName, lhs.activityInfo.name));
-            final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName(
-                    rhs.activityInfo.packageName, rhs.activityInfo.name));
-
-            if (lhsTarget != null && rhsTarget != null) {
-                final int selectProbabilityDiff = Float.compare(
-                        rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
-
-                if (selectProbabilityDiff != 0) {
-                    return selectProbabilityDiff > 0 ? 1 : -1;
-                }
-            }
-        }
-
-        CharSequence  sa = lhs.loadLabel(mPm);
-        if (sa == null) sa = lhs.activityInfo.name;
-        CharSequence  sb = rhs.loadLabel(mPm);
-        if (sb == null) sb = rhs.activityInfo.name;
-
-        return mCollator.compare(sa.toString().trim(), sb.toString().trim());
+        return mComparatorModel.getComparator().compare(lhs, rhs);
     }
 
     @Override
     public float getScore(ComponentName name) {
-        final ResolverTarget target = mTargetsDict.get(name);
-        if (target != null) {
-            return target.getSelectProbability();
-        }
-        return 0;
-    }
-
-    @Override
-    List<ComponentName> getTopComponentNames(int topK) {
-        return mTargetsDict.entrySet().stream()
-                .sorted((o1, o2) -> -Float.compare(getScore(o1.getKey()), getScore(o2.getKey())))
-                .limit(topK)
-                .map(Map.Entry::getKey)
-                .collect(Collectors.toList());
+        return mComparatorModel.getScore(name);
     }
 
     // update ranking model when the connection to it is valid.
     @Override
     public void updateModel(ComponentName componentName) {
         synchronized (mLock) {
-            if (mRanker != null) {
-                try {
-                    int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet())
-                            .indexOf(componentName);
-                    if (selectedPos >= 0 && mTargets != null) {
-                        final float selectedProbability = getScore(componentName);
-                        int order = 0;
-                        for (ResolverTarget target : mTargets) {
-                            if (target.getSelectProbability() > selectedProbability) {
-                                order++;
-                            }
-                        }
-                        logMetrics(order);
-                        mRanker.train(mTargets, selectedPos);
-                    } else {
-                        if (DEBUG) {
-                            Log.d(TAG, "Selected a unknown component: " + componentName);
-                        }
-                    }
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error in Train: " + e);
-                }
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "Ranker is null; skip updateModel.");
-                }
-            }
+            mComparatorModel.notifyOnTargetSelected(componentName);
         }
     }
 
@@ -313,19 +259,6 @@
         }
     }
 
-    // records metrics for evaluation.
-    private void logMetrics(int selectedPos) {
-        if (mRankerServiceName != null) {
-            MetricsLogger metricsLogger = new MetricsLogger();
-            LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED);
-            log.setComponentName(mRankerServiceName);
-            int isCategoryUsed = (mAnnotations == null) ? 0 : 1;
-            log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, isCategoryUsed);
-            log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos);
-            metricsLogger.write(log);
-        }
-    }
-
     // connect to a ranking service.
     private void initRanker(Context context) {
         synchronized (mLock) {
@@ -426,6 +359,7 @@
             }
             synchronized (mLock) {
                 mRanker = IResolverRankerService.Stub.asInterface(service);
+                mComparatorModel = buildUpdatedModel();
                 mConnectSignal.countDown();
             }
         }
@@ -443,6 +377,7 @@
         public void destroy() {
             synchronized (mLock) {
                 mRanker = null;
+                mComparatorModel = buildUpdatedModel();
             }
         }
     }
@@ -453,6 +388,7 @@
         mTargetsDict.clear();
         mTargets = null;
         mRankerServiceName = new ComponentName(mContext, this.getClass());
+        mComparatorModel = buildUpdatedModel();
         mResolvedRankerName = null;
         initRanker(mContext);
     }
@@ -508,4 +444,155 @@
         }
         return false;
     }
+
+    /**
+     * Re-construct a {@code ResolverRankerServiceComparatorModel} to replace the current model
+     * instance (if any) using the up-to-date {@code ResolverRankerServiceResolverComparator} ivar
+     * values.
+     *
+     * TODO: each time we replace the model instance, we're either updating the model to use
+     * adjusted data (which is appropriate), or we're providing a (late) value for one of our ivars
+     * that wasn't available the last time the model was updated. For those latter cases, we should
+     * just avoid creating the model altogether until we have all the prerequisites we'll need. Then
+     * we can probably simplify the logic in {@code ResolverRankerServiceComparatorModel} since we
+     * won't need to handle edge cases when the model data isn't fully prepared.
+     * (In some cases, these kinds of "updates" might interleave -- e.g., we might have finished
+     * initializing the first time and now want to adjust some data, but still need to wait for
+     * changes to propagate to the other ivars before rebuilding the model.)
+     */
+    private ResolverRankerServiceComparatorModel buildUpdatedModel() {
+        // TODO: we don't currently guarantee that the underlying target list/map won't be mutated,
+        // so the ResolverComparatorModel may provide inconsistent results. We should make immutable
+        // copies of the data (waiting for any necessary remaining data before creating the model).
+        return new ResolverRankerServiceComparatorModel(
+                mStats,
+                mTargetsDict,
+                mTargets,
+                mCollator,
+                mRanker,
+                mRankerServiceName,
+                (mAnnotations != null),
+                mPm);
+    }
+
+    /**
+     * Implementation of a {@code ResolverComparatorModel} that provides the same ranking logic as
+     * the legacy {@code ResolverRankerServiceResolverComparator}, as a refactoring step toward
+     * removing the complex legacy API.
+     */
+    static class ResolverRankerServiceComparatorModel implements ResolverComparatorModel {
+        private final Map<String, UsageStats> mStats;  // Treat as immutable.
+        private final Map<ComponentName, ResolverTarget> mTargetsDict;  // Treat as immutable.
+        private final List<ResolverTarget> mTargets;  // Treat as immutable.
+        private final Collator mCollator;
+        private final IResolverRankerService mRanker;
+        private final ComponentName mRankerServiceName;
+        private final boolean mAnnotationsUsed;
+        private final PackageManager mPm;
+
+        // TODO: it doesn't look like we should have to pass both targets and targetsDict, but it's
+        // not written in a way that makes it clear whether we can derive one from the other (at
+        // least in this constructor).
+        ResolverRankerServiceComparatorModel(
+                Map<String, UsageStats> stats,
+                Map<ComponentName, ResolverTarget> targetsDict,
+                List<ResolverTarget> targets,
+                Collator collator,
+                IResolverRankerService ranker,
+                ComponentName rankerServiceName,
+                boolean annotationsUsed,
+                PackageManager pm) {
+            mStats = stats;
+            mTargetsDict = targetsDict;
+            mTargets = targets;
+            mCollator = collator;
+            mRanker = ranker;
+            mRankerServiceName = rankerServiceName;
+            mAnnotationsUsed = annotationsUsed;
+            mPm = pm;
+        }
+
+        @Override
+        public Comparator<ResolveInfo> getComparator() {
+            // TODO: doCompute() doesn't seem to be concerned about null-checking mStats. Is that
+            // a bug there, or do we have a way of knowing it will be non-null under certain
+            // conditions?
+            return (lhs, rhs) -> {
+                if (mStats != null) {
+                    final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName(
+                            lhs.activityInfo.packageName, lhs.activityInfo.name));
+                    final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName(
+                            rhs.activityInfo.packageName, rhs.activityInfo.name));
+
+                    if (lhsTarget != null && rhsTarget != null) {
+                        final int selectProbabilityDiff = Float.compare(
+                                rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
+
+                        if (selectProbabilityDiff != 0) {
+                            return selectProbabilityDiff > 0 ? 1 : -1;
+                        }
+                    }
+                }
+
+                CharSequence  sa = lhs.loadLabel(mPm);
+                if (sa == null) sa = lhs.activityInfo.name;
+                CharSequence  sb = rhs.loadLabel(mPm);
+                if (sb == null) sb = rhs.activityInfo.name;
+
+                return mCollator.compare(sa.toString().trim(), sb.toString().trim());
+            };
+        }
+
+        @Override
+        public float getScore(ComponentName name) {
+            final ResolverTarget target = mTargetsDict.get(name);
+            if (target != null) {
+                return target.getSelectProbability();
+            }
+            return 0;
+        }
+
+        @Override
+        public void notifyOnTargetSelected(ComponentName componentName) {
+            if (mRanker != null) {
+                try {
+                    int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet())
+                            .indexOf(componentName);
+                    if (selectedPos >= 0 && mTargets != null) {
+                        final float selectedProbability = getScore(componentName);
+                        int order = 0;
+                        for (ResolverTarget target : mTargets) {
+                            if (target.getSelectProbability() > selectedProbability) {
+                                order++;
+                            }
+                        }
+                        logMetrics(order);
+                        mRanker.train(mTargets, selectedPos);
+                    } else {
+                        if (DEBUG) {
+                            Log.d(TAG, "Selected a unknown component: " + componentName);
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error in Train: " + e);
+                }
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "Ranker is null; skip updateModel.");
+                }
+            }
+        }
+
+        /** Records metrics for evaluation. */
+        private void logMetrics(int selectedPos) {
+            if (mRankerServiceName != null) {
+                MetricsLogger metricsLogger = new MetricsLogger();
+                LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED);
+                log.setComponentName(mRankerServiceName);
+                log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed);
+                log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos);
+                metricsLogger.write(log);
+            }
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index 43dacd7..354eb62 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -71,7 +71,7 @@
             new SynchronizedPool<>(Runtime.getRuntime().availableProcessors());
 
     private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
-    private static final float BLUR_FACTOR = 0.5f / 48;
+    private static final float BLUR_FACTOR = 1.5f / 48;
 
     private Context mContext;
     private Canvas mCanvas;
@@ -650,8 +650,8 @@
     /* Shadow generator block */
 
     private static final float KEY_SHADOW_DISTANCE = 1f / 48;
-    private static final int KEY_SHADOW_ALPHA = 61;
-    private static final int AMBIENT_SHADOW_ALPHA = 30;
+    private static final int KEY_SHADOW_ALPHA = 10;
+    private static final int AMBIENT_SHADOW_ALPHA = 7;
 
     private Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
     private Paint mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 8847a49..5541558 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -47,6 +47,7 @@
      * @param appInfo  representing the affected app
      * @throws SecurityException if logging is not allowed
      */
+    @EnforcePermission("LOG_COMPAT_CHANGE")
     void reportChange(long changeId, in ApplicationInfo appInfo);
 
     /**
@@ -60,6 +61,7 @@
      * @param packageName the package name of the app in question
      * @throws SecurityException if logging is not allowed
      */
+    @EnforcePermission("LOG_COMPAT_CHANGE")
     void reportChangeByPackageName(long changeId, in String packageName, int userId);
 
     /**
@@ -72,6 +74,7 @@
      * @param uid      the UID of the app in question
      * @throws SecurityException if logging is not allowed
      */
+    @EnforcePermission("LOG_COMPAT_CHANGE")
     void reportChangeByUid(long changeId, int uid);
 
     /**
@@ -90,6 +93,7 @@
      * @return {@code true} if the change is enabled for the current app
      * @throws SecurityException if logging or reading compat confis is not allowed
      */
+    @EnforcePermission(allOf={"LOG_COMPAT_CHANGE", "READ_COMPAT_CHANGE_CONFIG"})
     boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo);
 
     /**
@@ -115,6 +119,7 @@
      * @return {@code true} if the change is enabled for the current app
      * @throws SecurityException if logging or reading compat confis is not allowed
      */
+    @EnforcePermission(allOf={"LOG_COMPAT_CHANGE", "READ_COMPAT_CHANGE_CONFIG"})
     boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId);
 
     /**
@@ -140,6 +145,7 @@
      * @return {@code true} if the change is enabled for the current app
      * @throws SecurityException if logging or reading compat confis is not allowed
      */
+    @EnforcePermission(allOf={"LOG_COMPAT_CHANGE", "READ_COMPAT_CHANGE_CONFIG"})
     boolean isChangeEnabledByUid(long changeId, int uid);
 
     /**
@@ -151,6 +157,7 @@
      * @param packageName the package name of the app whose changes will be overridden
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG")
     void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
 
     /**
@@ -171,6 +178,7 @@
      *                           on specific apps by their package name
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD")
     void putAllOverridesOnReleaseBuilds(in CompatibilityOverridesByPackageConfig overridesByPackage);
 
     /**
@@ -190,6 +198,7 @@
      * @param packageName the package name of the app whose changes will be overridden
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD")
     void putOverridesOnReleaseBuilds(in CompatibilityOverrideConfig overrides, in String packageName);
 
     /**
@@ -201,6 +210,7 @@
      * @param packageName the package name of the app whose changes will be overridden
      * @throws SecurityException if overriding changes is not permitted.
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG")
     void setOverridesForTest(in CompatibilityChangeConfig overrides, in String packageName);
 
     /**
@@ -213,6 +223,7 @@
      * @return {@code true} if an override existed
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG")
     boolean clearOverride(long changeId, String packageName);
 
     /**
@@ -225,6 +236,7 @@
      * @return {@code true} if an override existed
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG")
     boolean clearOverrideForTest(long changeId, String packageName);
 
     /**
@@ -245,6 +257,7 @@
      *                                   removed for specific apps by their package name
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD")
     void removeAllOverridesOnReleaseBuilds(in CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage);
 
     /**
@@ -266,6 +279,7 @@
      *                            default behaviour
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD")
     void removeOverridesOnReleaseBuilds(in CompatibilityOverridesToRemoveConfig overridesToRemove, in String packageName);
 
     /**
@@ -280,6 +294,7 @@
      * @return The number of changes that were enabled.
      * @throws SecurityException if overriding changes is not permitted.
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG")
     int enableTargetSdkChanges(in String packageName, int targetSdkVersion);
 
     /**
@@ -294,6 +309,7 @@
      * @return the number of changes that were disabled
      * @throws SecurityException if overriding changes is not permitted.
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG")
     int disableTargetSdkChanges(in String packageName, int targetSdkVersion);
 
     /**
@@ -304,6 +320,7 @@
      * @param packageName the package name of the app whose overrides will be cleared
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG")
     void clearOverrides(in String packageName);
 
     /**
@@ -314,6 +331,7 @@
      * @param packageName the package name of the app whose overrides will be cleared
      * @throws SecurityException if overriding changes is not permitted
      */
+    @EnforcePermission("OVERRIDE_COMPAT_CHANGE_CONFIG")
     void clearOverridesForTest(in String packageName);
 
     /**
@@ -323,6 +341,7 @@
      * @return a {@link CompatibilityChangeConfig}, representing whether a change is enabled for
      * the given app or not
      */
+    @EnforcePermission(allOf={"LOG_COMPAT_CHANGE", "READ_COMPAT_CHANGE_CONFIG"})
     CompatibilityChangeConfig getAppConfig(in ApplicationInfo appInfo);
 
     /**
@@ -330,6 +349,7 @@
      *
      * @return an array of {@link CompatibilityChangeInfo} known to the service
      */
+    @EnforcePermission("READ_COMPAT_CHANGE_CONFIG")
     CompatibilityChangeInfo[] listAllChanges();
 
     /**
@@ -338,10 +358,12 @@
      *
      * @return an array of {@link CompatibilityChangeInfo}
      */
+    @RequiresNoPermission
     CompatibilityChangeInfo[] listUIChanges();
 
     /**
      * Gets an instance that can determine whether a changeid can be overridden for a package name.
      */
+    @RequiresNoPermission
     IOverrideValidator getOverrideValidator();
 }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index f19bfc6..daf2cf0 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -520,6 +520,13 @@
     public static final String USE_UNBUNDLED_SHARESHEET = "use_unbundled_sharesheet";
 
     /**
+     * (int) The delay (in ms) before refreshing the Sharesheet UI after a change to the share
+     * target data model. For more info see go/sharesheet-list-view-update-delay.
+     */
+    public static final String SHARESHEET_LIST_VIEW_UPDATE_DELAY =
+            "sharesheet_list_view_update_delay";
+
+    /**
      * (string) Name of the default QR code scanner activity. On the eligible devices this activity
      * is provided by GMS core.
      */
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index a60b310..73a4f1c 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -62,7 +62,9 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Deque;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
@@ -232,9 +234,9 @@
             throw new FileNotFoundException(doc + " is not found under " + parent);
         }
 
-        LinkedList<String> path = new LinkedList<>();
+        List<String> path = new ArrayList<>();
         while (doc != null && FileUtils.contains(parent, doc)) {
-            path.addFirst(getDocIdForFile(doc));
+            path.add(0, getDocIdForFile(doc));
 
             doc = doc.getParentFile();
         }
@@ -448,10 +450,10 @@
             File folder, String[] projection, Set<String> exclusion, Bundle queryArgs)
             throws FileNotFoundException {
         final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
-        final LinkedList<File> pending = new LinkedList<>();
+        final List<File> pending = new ArrayList<>();
         pending.add(folder);
         while (!pending.isEmpty() && result.getCount() < 24) {
-            final File file = pending.removeFirst();
+            final File file = pending.remove(0);
             if (shouldHide(file)) continue;
 
             if (file.isDirectory()) {
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index b786526..828e5cf 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -183,7 +183,7 @@
                 final ParsedOverlayInfo p = partitionOverlayInfos.get(j);
                 if (p.isStatic) {
                     partitionConfigs.add(new ParsedConfiguration(p.packageName,
-                            true /* enabled */, false /* mutable */, partition.policy, p));
+                            true /* enabled */, false /* mutable */, partition.policy, p, null));
                 }
             }
 
diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java
index 0ab7b3d..5ab77d8 100644
--- a/core/java/com/android/internal/content/om/OverlayConfigParser.java
+++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.content.pm.PackagePartitions;
 import android.content.pm.PackagePartitions.SystemPartition;
+import android.os.Build;
 import android.os.FileUtils;
 import android.util.ArraySet;
 import android.util.Log;
@@ -56,6 +57,34 @@
  **/
 final class OverlayConfigParser {
 
+    /** Represents a part of a parsed overlay configuration XML file. */
+    public static class ParsedConfigFile {
+        @NonNull public final String path;
+        @NonNull public final int line;
+        @Nullable public final String xml;
+
+        ParsedConfigFile(@NonNull String path, int line, @Nullable String xml) {
+            this.path = path;
+            this.line = line;
+            this.xml = xml;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(getClass().getSimpleName());
+            sb.append("{path=");
+            sb.append(path);
+            sb.append(", line=");
+            sb.append(line);
+            if (xml != null) {
+                sb.append(", xml=");
+                sb.append(xml);
+            }
+            sb.append("}");
+            return sb.toString();
+        }
+    }
+
     // Default values for overlay configurations.
     static final boolean DEFAULT_ENABLED_STATE = false;
     static final boolean DEFAULT_MUTABILITY = true;
@@ -94,24 +123,40 @@
         @NonNull
         public final String policy;
 
-        /** Information extracted from the manifest of the overlay. */
-        @NonNull
+        /**
+         * Information extracted from the manifest of the overlay.
+         * Null if the information was read from a config file instead of a manifest.
+         *
+         * @see parsedConfigFile
+         **/
+        @Nullable
         public final ParsedOverlayInfo parsedInfo;
 
+        /**
+         * The config file used to configure this overlay.
+         * Null if no config file was used, in which case the overlay's manifest was used instead.
+         *
+         * @see parsedInfo
+         **/
+        @Nullable
+        public final ParsedConfigFile parsedConfigFile;
+
         ParsedConfiguration(@NonNull String packageName, boolean enabled, boolean mutable,
-                @NonNull String policy, @NonNull ParsedOverlayInfo parsedInfo) {
+                @NonNull String policy, @Nullable ParsedOverlayInfo parsedInfo,
+                @Nullable ParsedConfigFile parsedConfigFile) {
             this.packageName = packageName;
             this.enabled = enabled;
             this.mutable = mutable;
             this.policy = policy;
             this.parsedInfo = parsedInfo;
+            this.parsedConfigFile = parsedConfigFile;
         }
 
         @Override
         public String toString() {
             return getClass().getSimpleName() + String.format("{packageName=%s, enabled=%s"
-                            + ", mutable=%s, policy=%s, parsedInfo=%s}", packageName, enabled,
-                    mutable, policy, parsedInfo);
+                            + ", mutable=%s, policy=%s, parsedInfo=%s, parsedConfigFile=%s}",
+                    packageName, enabled, mutable, policy, parsedInfo, parsedConfigFile);
         }
     }
 
@@ -417,9 +462,26 @@
             Log.w(TAG, "found default-disabled immutable overlay " + packageName);
         }
 
-        final ParsedConfiguration Config = new ParsedConfiguration(packageName, isEnabled,
-                isMutable, parsingContext.mPartition.policy, info);
+        final ParsedConfigFile parsedConfigFile = new ParsedConfigFile(
+                configFile.getPath().intern(), parser.getLineNumber(),
+                (Build.IS_ENG || Build.IS_USERDEBUG) ? currentParserContextToString(parser) : null);
+        final ParsedConfiguration config = new ParsedConfiguration(packageName, isEnabled,
+                isMutable, parsingContext.mPartition.policy, info, parsedConfigFile);
         parsingContext.mConfiguredOverlays.add(packageName);
-        parsingContext.mOrderedConfigurations.add(Config);
+        parsingContext.mOrderedConfigurations.add(config);
+    }
+
+    private static String currentParserContextToString(@NonNull XmlPullParser parser) {
+        StringBuilder sb = new StringBuilder("<");
+        sb.append(parser.getName());
+        sb.append(" ");
+        for (int i = 0; i < parser.getAttributeCount(); i++) {
+            sb.append(parser.getAttributeName(i));
+            sb.append("=\"");
+            sb.append(parser.getAttributeValue(i));
+            sb.append("\" ");
+        }
+        sb.append("/>");
+        return sb.toString();
     }
 }
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 2ee47b6..4babb70 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -34,7 +34,7 @@
     void setInputMethod(String id, in AndroidFuture future /* T=Void */);
     void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype,
             in AndroidFuture future /* T=Void */);
-    void hideMySoftInput(int flags, in AndroidFuture future /* T=Void */);
+    void hideMySoftInput(int flags, int reason, in AndroidFuture future /* T=Void */);
     void showMySoftInput(int flags, in AndroidFuture future /* T=Void */);
     void updateStatusIconAsync(String packageName, int iconId);
     void switchToPreviousInputMethod(in AndroidFuture future /* T=Boolean */);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index d669768..97ad084 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -194,12 +194,12 @@
                 return "SHOW_SOFT_INPUT";
             case SoftInputShowHideReason.ATTACH_NEW_INPUT:
                 return "ATTACH_NEW_INPUT";
-            case SoftInputShowHideReason.SHOW_MY_SOFT_INPUT:
-                return "SHOW_MY_SOFT_INPUT";
+            case SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME:
+                return "SHOW_SOFT_INPUT_FROM_IME";
             case SoftInputShowHideReason.HIDE_SOFT_INPUT:
                 return "HIDE_SOFT_INPUT";
-            case SoftInputShowHideReason.HIDE_MY_SOFT_INPUT:
-                return "HIDE_MY_SOFT_INPUT";
+            case SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME:
+                return "HIDE_SOFT_INPUT_FROM_IME";
             case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV:
                 return "SHOW_AUTO_EDITOR_FORWARD_NAV";
             case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV:
@@ -242,6 +242,16 @@
                 return "SHOW_SOFT_INPUT_BY_INSETS_API";
             case SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE:
                 return "HIDE_DISPLAY_IME_POLICY_HIDE";
+            case SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API:
+                return "HIDE_SOFT_INPUT_BY_INSETS_API";
+            case SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY:
+                return "HIDE_SOFT_INPUT_BY_BACK_KEY";
+            case SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT:
+                return "HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT";
+            case SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED:
+                return "HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED";
+            case SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION:
+                return "HIDE_SOFT_INPUT_IMM_DEPRECATION";
             default:
                 return "Unknown=" + reason;
         }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 15d7acf..67c2103 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -253,18 +253,19 @@
      * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, IVoidResultCallback)}
      *
      * @param flags additional operating flags
+     * @param reason the reason to hide soft input
      * @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY
      * @see android.view.inputmethod.InputMethodManager#HIDE_NOT_ALWAYS
      */
     @AnyThread
-    public void hideMySoftInput(int flags) {
+    public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason) {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
         }
         try {
             final AndroidFuture<Void> future = new AndroidFuture<>();
-            ops.hideMySoftInput(flags, future);
+            ops.hideMySoftInput(flags, reason, future);
             CompletableFutureUtil.getResult(future);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 9e57762..97ad5cb 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -19,8 +19,11 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
+import android.os.IBinder;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
 
 import java.lang.annotation.Retention;
 
@@ -31,9 +34,9 @@
 @IntDef(value = {
         SoftInputShowHideReason.SHOW_SOFT_INPUT,
         SoftInputShowHideReason.ATTACH_NEW_INPUT,
-        SoftInputShowHideReason.SHOW_MY_SOFT_INPUT,
+        SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME,
         SoftInputShowHideReason.HIDE_SOFT_INPUT,
-        SoftInputShowHideReason.HIDE_MY_SOFT_INPUT,
+        SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME,
         SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV,
         SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV,
         SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE,
@@ -55,7 +58,12 @@
         SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT,
         SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT,
         SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API,
-        SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE})
+        SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE,
+        SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API,
+        SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_BACK_KEY,
+        SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
+        SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED,
+        SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION})
 public @interface SoftInputShowHideReason {
     /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
     int SHOW_SOFT_INPUT = 0;
@@ -63,8 +71,12 @@
     /** Show soft input when {@code InputMethodManagerService#attachNewInputLocked} called. */
     int ATTACH_NEW_INPUT = 1;
 
-    /** Show soft input by {@code InputMethodManagerService#showMySoftInput}. */
-    int SHOW_MY_SOFT_INPUT = 2;
+    /** Show soft input by {@code InputMethodManagerService#showMySoftInput}. This is triggered when
+     *  the IME process try to show the keyboard.
+     *
+     * @see android.inputmethodservice.InputMethodService#requestShowSelf(int)
+     */
+    int SHOW_SOFT_INPUT_FROM_IME = 2;
 
     /**
      * Hide soft input by
@@ -72,8 +84,11 @@
      */
     int HIDE_SOFT_INPUT = 3;
 
-    /** Hide soft input by {@code InputMethodManagerService#hideMySoftInput}. */
-    int HIDE_MY_SOFT_INPUT = 4;
+    /**
+     * Hide soft input by
+     * {@link android.inputmethodservice.InputMethodService#requestHideSelf(int)}.
+     */
+    int HIDE_SOFT_INPUT_FROM_IME = 4;
 
     /**
      * Show soft input when navigated forward to the window (with
@@ -203,4 +218,32 @@
      * See also {@code InputMethodManagerService#mImeHiddenByDisplayPolicy}.
      */
     int HIDE_DISPLAY_IME_POLICY_HIDE = 26;
+
+    /**
+     * Hide soft input by {@link android.view.InsetsController#hide(int)}.
+     */
+    int HIDE_SOFT_INPUT_BY_INSETS_API = 27;
+
+    /**
+     * Hide soft input by {@link android.inputmethodservice.InputMethodService#handleBack(boolean)}.
+     */
+    int HIDE_SOFT_INPUT_BY_BACK_KEY = 28;
+
+    /**
+     * Hide soft input by
+     * {@link android.inputmethodservice.InputMethodService#onToggleSoftInput(int, int)}.
+     */
+    int HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT = 29;
+
+    /**
+     * Hide soft input by
+     * {@link android.inputmethodservice.InputMethodService#onExtractingInputChanged(EditorInfo)})}.
+     */
+    int HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED = 30;
+
+    /**
+     * Hide soft input by the deprecated
+     * {@link InputMethodManager#hideSoftInputFromInputMethod(IBinder, int)}.
+     */
+    int HIDE_SOFT_INPUT_IMM_DEPRECATION = 31;
 }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 488fb180..12571b1 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -912,6 +912,12 @@
             }
         }
 
+        if (!st.hasPanelItems()) {
+            // Ensure that |st.decorView| has its actual content. Otherwise, an empty window can be
+            // created and cause ANR.
+            return;
+        }
+
         st.isHandled = false;
 
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index bf3e8d5..a0f7905 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -1,11 +1,5 @@
 package com.android.internal.view.menu;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
 import android.annotation.AttrRes;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -22,16 +16,16 @@
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.ViewTreeObserver;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnKeyListener;
+import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.AbsListView;
 import android.widget.FrameLayout;
 import android.widget.HeaderViewListAdapter;
 import android.widget.ListAdapter;
-import android.widget.MenuItemHoverListener;
 import android.widget.ListView;
+import android.widget.MenuItemHoverListener;
 import android.widget.MenuPopupWindow;
 import android.widget.PopupWindow;
 import android.widget.PopupWindow.OnDismissListener;
@@ -40,6 +34,11 @@
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A popup for a menu which will allow multiple submenus to appear in a cascading fashion, side by
  * side.
@@ -70,7 +69,7 @@
     private final Handler mSubMenuHoverHandler;
 
     /** List of menus that were added before this popup was shown. */
-    private final List<MenuBuilder> mPendingMenus = new LinkedList<>();
+    private final List<MenuBuilder> mPendingMenus = new ArrayList<>();
 
     /**
      * List of open menus. The first item is the root menu and each
diff --git a/core/java/com/android/internal/widget/ConversationHeaderLinearLayout.java b/core/java/com/android/internal/widget/ConversationHeaderLinearLayout.java
index 481183e..c1e2840 100644
--- a/core/java/com/android/internal/widget/ConversationHeaderLinearLayout.java
+++ b/core/java/com/android/internal/widget/ConversationHeaderLinearLayout.java
@@ -23,6 +23,7 @@
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -96,7 +97,7 @@
                 continue;
             }
             if (visibleChildrenToShorten == null) {
-                visibleChildrenToShorten = new LinkedList<>();
+                visibleChildrenToShorten = new ArrayList<>(count);
             }
             visibleChildrenToShorten.add(new ViewInfo(child));
             remainingWeight += Math.max(0, weight);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a94b307..6771cdf 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -39,7 +39,6 @@
 import android.content.pm.UserInfo;
 import android.os.Build;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
@@ -47,7 +46,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.text.TextUtils;
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index 8c61a12..95a4e12 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -61,10 +61,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -950,9 +950,9 @@
 
         int availableWidth = toolbarWidth;
 
-        final LinkedList<MenuItem> remainingMenuItems = new LinkedList<>();
+        final ArrayList<MenuItem> remainingMenuItems = new ArrayList<>();
         // add the overflow menu items to the end of the remainingMenuItems list.
-        final LinkedList<MenuItem> overflowMenuItems = new LinkedList();
+        final ArrayList<MenuItem> overflowMenuItems = new ArrayList<>();
         for (MenuItem menuItem : menuItems) {
             if (menuItem.getItemId() != android.R.id.textAssist
                     && menuItem.requiresOverflow()) {
@@ -969,7 +969,7 @@
         int lastGroupId = -1;
         boolean isFirstItem = true;
         while (!remainingMenuItems.isEmpty()) {
-            final MenuItem menuItem = remainingMenuItems.peek();
+            final MenuItem menuItem = remainingMenuItems.get(0);
 
             // if this is the first item, regardless of requiresOverflow(), it should be
             // displayed on the main panel. Otherwise all items including this one will be
@@ -1022,7 +1022,7 @@
                 params.width = menuItemButtonWidth;
                 menuItemButton.setLayoutParams(params);
                 availableWidth -= menuItemButtonWidth;
-                remainingMenuItems.pop();
+                remainingMenuItems.remove(0);
             } else {
                 break;
             }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 518fc09..b3017031 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -2036,7 +2036,7 @@
 }
 
 static jint nativeGetGPUContextPriority(JNIEnv* env, jclass clazz) {
-    return static_cast<jint>(SurfaceComposerClient::getGPUContextPriority());
+    return static_cast<jint>(SurfaceComposerClient::getGpuContextPriority());
 }
 
 static void nativeSetTransformHint(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl,
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index a4463e4..9070933 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -16,6 +16,7 @@
 per-file package_item_info.proto = toddke@google.com,patb@google.com
 per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS
 per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
+per-file android/hardware/sensorprivacy.proto = ntmyren@google.com,evanseverson@google.com,ewol@google.com
 
 # Biometrics
 jaggies@google.com
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4075c5f..fc7b634 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2278,7 +2278,8 @@
         android:protectionLevel="signature|role" />
 
     <!-- Allows bluetooth stack to access files
-         @hide This should only be used by Bluetooth apk.
+         This should only be granted to the Bluetooth apk.
+         @hide @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)
     -->
     <permission android:name="android.permission.BLUETOOTH_STACK"
         android:protectionLevel="signature|role" />
@@ -3735,12 +3736,6 @@
     <permission android:name="android.permission.BIND_ATTESTATION_VERIFICATION_SERVICE"
                 android:protectionLevel="signature" />
 
-    <!-- Allows the caller to generate keymint keys with the INCLUDE_UNIQUE_ID tag, which
-         uniquely identifies the device via the attestation certificate.
-         @hide @TestApi -->
-    <permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION"
-         android:protectionLevel="signature" />
-
     <!-- ========================================= -->
     <!-- Permissions for special development tools -->
     <!-- ========================================= -->
@@ -4407,13 +4402,13 @@
     <permission android:name="android.permission.TUNER_RESOURCE_ACCESS"
          android:protectionLevel="signature|privileged|vendorPrivileged" />
 
-    <!-- This permission is required by Media Resource Manager Service when
-         accessing its overridePid Api.
-         <p>Protection level: signature
+    <!-- @SystemApi This permission is required by Media Resource Manager Service when
+         system services create MediaCodecs on behalf of other processes and apps.
+         <p>Protection level: signature|privileged|vendorPrivileged
          <p>Not for use by third-party applications.
          @hide -->
     <permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"
-         android:protectionLevel="signature" />
+         android:protectionLevel="signature|privileged|vendorPrivileged" />
     <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" />
 
     <!-- This permission is required by Media Resource Observer Service when
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 35bed2d..30fb45e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3736,6 +3736,9 @@
          "Guest" and "Reset guest". -->
     <bool name="config_guestUserAutoCreated">false</bool>
 
+    <!-- If true, owner can change guest user ephemeral state via UI option -->
+    <bool name="config_guestUserAllowEphemeralStateChange">true</bool>
+
     <!-- Enforce strong auth on boot. Setting this to false represents a security risk and should
          not be ordinarily done. The only case in which this might be permissible is in a car head
          unit where there are hardware mechanisms to protect the device (physical keys) and not
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f177226..61ca18c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -393,6 +393,7 @@
   <java-symbol type="bool" name="config_supportsInsecureLockScreen" />
   <java-symbol type="bool" name="config_guestUserEphemeral" />
   <java-symbol type="bool" name="config_guestUserAutoCreated" />
+  <java-symbol type="bool" name="config_guestUserAllowEphemeralStateChange" />
   <java-symbol type="bool" name="config_localDisplaysMirrorContent" />
   <java-symbol type="array" name="config_localPrivateDisplayPorts" />
   <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
diff --git a/core/res/res/xml-watch/default_zen_mode_config.xml b/core/res/res/xml-watch/default_zen_mode_config.xml
deleted file mode 100644
index 938cc0c..0000000
--- a/core/res/res/xml-watch/default_zen_mode_config.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright 2016, The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-
-<!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
-<zen version="2">
-    <!-- Allow starred contacts to go through only.
-    Repeated calls, calls, messages, reminders, events off. -->
-    <allow from="2" repeatCallers="false" calls="false" messages="false" reminders="false"
-           events="false"/>
-</zen>
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 0ebf03f..925da49 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -24,6 +24,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetricsInt;
+import android.graphics.text.LineBreakConfig;
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
 import android.text.Layout.Alignment;
@@ -925,4 +926,24 @@
         assertEquals(0, layout.getHeight(true));
         assertEquals(2, layout.getLineCount());
     }
+
+    @Test
+    public void testBuilder_autoPhraseBreaking() {
+        {
+            // setAutoPhraseBreaking true
+            LineBreakConfig lineBreakConfig = new LineBreakConfig.Builder()
+                    .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_STYLE_NONE)
+                    .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+                    .setAutoPhraseBreaking(true)
+                    .build();
+            final String text = "これが正解。";
+            // Obtain.
+            StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0,
+                    text.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+            builder.setLineBreakConfig(lineBreakConfig);
+            builder.build();
+            assertEquals(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE,
+                    builder.getLineBreakWordStyle());
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index e303934..b3b19ce 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -34,6 +34,8 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
 import android.view.HandwritingInitiator;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -61,23 +63,32 @@
 public class HandwritingInitiatorTest {
     private static final int TOUCH_SLOP = 8;
     private static final long TIMEOUT = ViewConfiguration.getLongPressTimeout();
+    private static final int HANDWRITING_AREA_PADDING_DIP = 20;
+
     private static final Rect sHwArea = new Rect(100, 200, 500, 500);
 
     private HandwritingInitiator mHandwritingInitiator;
     private View mTestView;
-    private  Context mContext;
+    private Context mContext;
+    private int mHandwritingAreaPaddingPx;
 
     @Before
     public void setup() {
         final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mContext = mInstrumentation.getTargetContext();
-        ViewConfiguration viewConfiguration = mock(ViewConfiguration.class);
+        final ViewConfiguration viewConfiguration = mock(ViewConfiguration.class);
         when(viewConfiguration.getScaledTouchSlop()).thenReturn(TOUCH_SLOP);
 
-        InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
-        mHandwritingInitiator =
-                spy(new HandwritingInitiator(viewConfiguration, inputMethodManager));
 
+        final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+        InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
+        mHandwritingInitiator = spy(new HandwritingInitiator(viewConfiguration, inputMethodManager,
+                displayMetrics));
+
+        mHandwritingAreaPaddingPx = Math.round(TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP,
+                HANDWRITING_AREA_PADDING_DIP,
+                displayMetrics));
         mTestView = createView(sHwArea, true);
         mHandwritingInitiator.updateHandwritingAreasForView(mTestView);
     }
@@ -127,6 +138,24 @@
     }
 
     @Test
+    public void onTouchEvent_startHandwriting_when_stylusMove_withinExtendedHWArea() {
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+        final int x1 = sHwArea.left - mHandwritingAreaPaddingPx / 2;
+        final int y1 = sHwArea.top - mHandwritingAreaPaddingPx / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP * 2;
+        final int y2 = y1;
+
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        // Stylus movement within extended HandwritingArea should trigger IMM.startHandwriting once.
+        verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
+    }
+
+    @Test
     public void onTouchEvent_startHandwriting_inputConnectionBuiltAfterStylusMove() {
         final int x1 = (sHwArea.left + sHwArea.right) / 2;
         final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
@@ -145,6 +174,24 @@
     }
 
     @Test
+    public void onTouchEvent_startHandwriting_inputConnectionBuilt_stylusMoveInExtendedHWArea() {
+        final int x1 = sHwArea.right + mHandwritingAreaPaddingPx / 2;
+        final int y1 = sHwArea.bottom + mHandwritingAreaPaddingPx / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP * 2;
+        final int y2 = y1;
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        // InputConnection is created after stylus movement.
+        mHandwritingInitiator.onInputConnectionCreated(mTestView);
+
+        verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
+    }
+
+    @Test
     public void onTouchEvent_notStartHandwriting_when_stylusTap_withinHWArea() {
         mHandwritingInitiator.onInputConnectionCreated(mTestView);
         final int x1 = 200;
@@ -213,6 +260,23 @@
     }
 
     @Test
+    public void onTouchEvent_focusView_stylusMoveOnce_withinExtendedHWArea() {
+        final int x1 = sHwArea.left - mHandwritingAreaPaddingPx / 2;
+        final int y1 = sHwArea.top - mHandwritingAreaPaddingPx / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + TOUCH_SLOP * 2;
+        final int y2 = y1;
+
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        // HandwritingInitiator will request focus for the registered view.
+        verify(mTestView, times(1)).requestFocus();
+    }
+
+    @Test
     public void autoHandwriting_whenDisabled_wontStartHW() {
         View mockView = createView(sHwArea, false);
         mHandwritingInitiator.onInputConnectionCreated(mockView);
diff --git a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
index 04b8886..3e640c1 100644
--- a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
@@ -115,11 +115,6 @@
 
             @Override
             void handleResultMessage(Message message) {}
-
-            @Override
-            List<ComponentName> getTopComponentNames(int topK) {
-                return null;
-            }
         };
         return testComparator;
     }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index cf78646..b38e1c2 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -81,7 +81,6 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.service.chooser.ChooserTarget;
-import android.util.Log;
 import android.view.View;
 
 import androidx.annotation.CallSuper;
@@ -623,7 +622,7 @@
         List<ResolvedComponentInfo> stableCopy =
                 createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
         waitForIdle();
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
 
         onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
                 .perform(click());
@@ -1437,7 +1436,7 @@
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
         // TODO: restructure the tests b/129870719
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
 
         assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
                 activity.getAdapter().getCount(), is(3));
@@ -1513,7 +1512,7 @@
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
         // TODO: restructure the tests b/129870719
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
 
         assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
                 activity.getAdapter().getCount(), is(3));
@@ -1595,7 +1594,7 @@
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
         // TODO: restructure the tests b/129870719
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
 
         assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
                 wrapper.getAdapter().getCount(), is(3));
@@ -1667,7 +1666,7 @@
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
         // TODO: restructure the tests b/129870719
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
 
         assertThat("Chooser should have 4 targets (2 apps, 2 direct)",
                 wrapper.getAdapter().getCount(), is(4));
@@ -1754,7 +1753,7 @@
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
         // TODO: restructure the tests b/129870719
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
 
         assertThat(
                 String.format("Chooser should have %d targets (%d apps, 1 direct, 15 A-Z)",
@@ -1879,12 +1878,13 @@
             return true;
         };
 
-        mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        final IChooserWrapper activity = (IChooserWrapper)
+                mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
         waitForIdle();
         onView(withTextFromRuntimeResource("resolver_work_tab")).perform(click());
         waitForIdle();
         // wait for the share sheet to expand
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
 
         onView(first(allOf(
                 withText(workResolvedComponentInfos.get(0)
@@ -2023,7 +2023,7 @@
                 .check(matches(isDisplayed()));
     }
 
-    @Test
+    @Test @Ignore("b/222124533")
     public void testAppTargetLogging() throws InterruptedException {
         Intent sendIntent = createSendTextIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -2042,6 +2042,10 @@
                 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
         waitForIdle();
 
+        // TODO(b/222124533): other test cases use a timeout to make sure that the UI is fully
+        // populated; without one, this test flakes. Ideally we should address the need for a
+        // timeout everywhere instead of introducing one to fix this particular test.
+
         assertThat(activity.getAdapter().getCount(), is(2));
         onView(withIdFromRuntimeResource("profile_button")).check(doesNotExist());
 
@@ -2143,7 +2147,7 @@
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
         // TODO: restructure the tests b/129870719
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+        Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
 
         assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
                 activity.getAdapter().getCount(), is(3));
@@ -2324,7 +2328,7 @@
         assertThat(logger.numCalls(), is(6));
     }
 
-    @Test
+    @Test @Ignore("b/222124533")
     public void testSwitchProfileLogging() throws InterruptedException {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -3069,8 +3073,15 @@
     // framework code on the device is up-to-date.
     // TODO: is there a better way to do this? (Other than abandoning inheritance-based DI wrapper?)
     private int getRuntimeResourceId(String name, String defType) {
-        int id = mActivityRule.getActivity().getResources().getIdentifier(name, defType, "android");
+        int id = -1;
+        if (ChooserActivityOverrideData.getInstance().resources != null) {
+            id = ChooserActivityOverrideData.getInstance().resources.getIdentifier(
+                  name, defType, "android");
+        } else {
+            id = mActivityRule.getActivity().getResources().getIdentifier(name, defType, "android");
+        }
         assertThat(id, greaterThan(0));
+
         return id;
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/FakeResolverComparatorModel.java b/core/tests/coretests/src/com/android/internal/app/FakeResolverComparatorModel.java
new file mode 100644
index 0000000..fbbe57c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/FakeResolverComparatorModel.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.ComponentName;
+import android.content.pm.ResolveInfo;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Basic {@link ResolverComparatorModel} implementation that sorts according to a pre-defined (or
+ * default) {@link java.util.Comparator}.
+ */
+public class FakeResolverComparatorModel implements ResolverComparatorModel {
+    private final Comparator<ResolveInfo> mComparator;
+
+    public static FakeResolverComparatorModel makeModelFromComparator(
+            Comparator<ResolveInfo> comparator) {
+        return new FakeResolverComparatorModel(comparator);
+    }
+
+    public static FakeResolverComparatorModel makeDefaultModel() {
+       return makeModelFromComparator(Comparator.comparing(ri -> ri.activityInfo.name));
+    }
+
+    @Override
+    public Comparator<ResolveInfo> getComparator() {
+        return mComparator;
+    }
+
+    @Override
+    public float getScore(ComponentName name) {
+        return 0.0f;  // Models are not required to provide numerical scores.
+    }
+
+    @Override
+    public void notifyOnTargetSelected(ComponentName componentName) {
+        System.out.println(
+                "User selected " + componentName + " under model " + System.identityHashCode(this));
+    }
+
+    private FakeResolverComparatorModel(Comparator<ResolveInfo> comparator) {
+        mComparator = comparator;
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index e7a23f2..43fba52 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -516,8 +516,6 @@
         onView(withText(R.string.resolver_work_tab))
                 .perform(click());
         waitForIdle();
-        // wait for the share sheet to expand
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
         onView(first(allOf(withText(workResolvedComponentInfos.get(0)
                 .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
                 .perform(click());
@@ -616,8 +614,6 @@
         onView(withText(R.string.resolver_work_tab))
                 .perform(click());
         waitForIdle();
-        // wait for the share sheet to expand
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
         onView(first(allOf(
                 withText(workResolvedComponentInfos.get(0)
                         .getResolveInfoAt(0).activityInfo.applicationInfo.name),
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index a927f53..8f32f0e 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -1,4 +1,3 @@
-
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -42,8 +41,10 @@
     static_libs: [
         "truth-prebuilt",
         "kxml2-2.3.0",
+        "compile-testing-prebuilt",
         "error_prone_android_framework_lib",
         "error_prone_test_helpers",
+        "google_java_format",
         "hamcrest-library",
         "hamcrest",
         "platform-test-annotations",
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
new file mode 100644
index 0000000..07f1d4a
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.LinkType.NONE;
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Description.NO_MATCH;
+import static com.google.errorprone.util.ASTHelpers.getStartPosition;
+import static com.google.errorprone.util.ASTHelpers.getSymbol;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.fixes.SuggestedFix;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.util.ASTHelpers;
+import com.google.errorprone.util.ErrorProneToken;
+import com.google.errorprone.util.ErrorProneTokens;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.parser.Tokens;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.lang.model.element.ElementKind;
+
+/**
+ * Bug checker to warn about {@code @hide} directives in comments.
+ *
+ * {@code @hide} tags are only meaningful inside of Javadoc comments. Errorprone has checks for
+ * standard Javadoc tags but doesn't know anything about {@code @hide} since it's an Android
+ * specific tag.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+        name = "AndroidHideInComments",
+        summary = "Warns when there are @hide declarations in comments rather than javadoc",
+        linkType = NONE,
+        severity = WARNING)
+public class HideInCommentsChecker extends BugChecker implements
+        BugChecker.CompilationUnitTreeMatcher {
+
+    @Override
+    public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
+        final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree);
+        final String sourceCode = state.getSourceCode().toString();
+        for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) {
+            for (Tokens.Comment comment : token.comments()) {
+                if (!javadocableTrees.containsKey(token.pos())) {
+                    continue;
+                }
+                generateFix(comment).ifPresent(fix -> {
+                    final Tree javadocableTree = javadocableTrees.get(token.pos());
+                    state.reportMatch(describeMatch(javadocableTree, fix));
+                });
+            }
+        }
+        // We might have multiple matches, so report them via VisitorState rather than the return
+        // value from the match function.
+        return NO_MATCH;
+    }
+
+    private static Optional<SuggestedFix> generateFix(Tokens.Comment comment) {
+        final String text = comment.getText();
+        if (text.startsWith("/**")) {
+            return Optional.empty();
+        }
+
+        if (!text.contains("@hide")) {
+            return Optional.empty();
+        }
+
+        if (text.startsWith("/*")) {
+            final int pos = comment.getSourcePos(1);
+            return Optional.of(SuggestedFix.replace(pos, pos, "*"));
+        } else if (text.startsWith("//")) {
+            final int endPos = comment.getSourcePos(text.length() - 1);
+            final char endChar = text.charAt(text.length() - 1);
+            String javadocClose = " */";
+            if (endChar != ' ') {
+                javadocClose = endChar + javadocClose;
+            }
+            final SuggestedFix fix = SuggestedFix.builder()
+                    .replace(comment.getSourcePos(1), comment.getSourcePos(2), "**")
+                    .replace(endPos, endPos + 1, javadocClose)
+                    .build();
+            return Optional.of(fix);
+        }
+
+        return Optional.empty();
+    }
+
+
+    private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree) {
+        Map<Integer, Tree> javadoccableTrees = new HashMap<>();
+        new SuppressibleTreePathScanner<Void, Void>() {
+            @Override
+            public Void visitClass(ClassTree classTree, Void unused) {
+                javadoccableTrees.put(getStartPosition(classTree), classTree);
+                return super.visitClass(classTree, null);
+            }
+
+            @Override
+            public Void visitMethod(MethodTree methodTree, Void unused) {
+                // Generated constructors never have comments
+                if (!ASTHelpers.isGeneratedConstructor(methodTree)) {
+                    javadoccableTrees.put(getStartPosition(methodTree), methodTree);
+                }
+                return super.visitMethod(methodTree, null);
+            }
+
+            @Override
+            public Void visitVariable(VariableTree variableTree, Void unused) {
+                ElementKind kind = getSymbol(variableTree).getKind();
+                if (kind == ElementKind.FIELD) {
+                    javadoccableTrees.put(getStartPosition(variableTree), variableTree);
+                }
+                if (kind == ElementKind.ENUM_CONSTANT) {
+                    javadoccableTrees.put(getStartPosition(variableTree), variableTree);
+                    if (variableTree.getInitializer() instanceof NewClassTree) {
+                        // Skip the generated class definition
+                        ClassTree classBody =
+                                ((NewClassTree) variableTree.getInitializer()).getClassBody();
+                        if (classBody != null) {
+                            scan(classBody.getMembers(), null);
+                        }
+                        return null;
+                    }
+                }
+                return super.visitVariable(variableTree, null);
+            }
+
+        }.scan(tree, null);
+        return javadoccableTrees;
+    }
+
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java
new file mode 100644
index 0000000..f3e6c727
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
+
+import com.google.errorprone.BugCheckerRefactoringTestHelper;
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class HideInCommentsCheckerTest {
+    private static final String REFACTORING_FILE = "Test.java";
+
+    private BugCheckerRefactoringTestHelper mRefactoringHelper;
+    private CompilationTestHelper mCompilationHelper;
+
+    @Before
+    public void setUp() {
+        mRefactoringHelper = BugCheckerRefactoringTestHelper.newInstance(
+                HideInCommentsChecker.class, HideInCommentsCheckerTest.class);
+        mCompilationHelper = CompilationTestHelper.newInstance(
+                HideInCommentsChecker.class, HideInCommentsCheckerTest.class);
+    }
+
+
+    @Test
+    public void refactorSingleLineComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  // Foo @hide",
+                        "  void foo() {}",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorSingleLineComment_doesntAddUnnecessarySpace() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  // Foo @hide ",
+                        "  void foo() {}",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorSingleLineBlockComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /* Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorMultiLineBlockComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /*",
+                        "   * Foo.",
+                        "   *",
+                        "   * @hide",
+                        "   */",
+                        "  void foo(int foo) {}",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /**",
+                        "   * Foo.",
+                        "   *",
+                        "   * @hide",
+                        "   */",
+                        "  void foo(int foo) {}",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorFieldComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /* Foo @hide */",
+                        "  public int foo = 0;",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  public int foo = 0;",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorClassComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "/* Foo @hide */",
+                        "public class Test {}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "/** Foo @hide */",
+                        "public class Test {}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorEnumComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public enum Test {",
+                        "  /* Foo @hide */",
+                        "  FOO",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public enum Test {",
+                        "  /** Foo @hide */",
+                        "  FOO",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void canBeSuppressed() {
+        mCompilationHelper
+                .addSourceLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /* Foo @hide */",
+                        "  @SuppressWarnings(\"AndroidHideInComments\")",
+                        "  void foo() {}",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void isInJavadoc() {
+        mCompilationHelper
+                .addSourceLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void isInMultilineJavadoc() {
+        mCompilationHelper
+                .addSourceLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /**",
+                        "   * Foo.",
+                        "   *",
+                        "   * @hide",
+                        "   */",
+                        "  void foo(int foo) {}",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void noHidePresent() {
+        mCompilationHelper
+                .addSourceLines(
+                        "test/" + REFACTORING_FILE,
+                        "package test;",
+                        "// Foo.",
+                        "public class Test {",
+                        "  // Foo.",
+                        "  public int a;",
+                        "  /*",
+                        "   * Foo.",
+                        "   *",
+                        "   */",
+                        "  void foo(int foo) {}",
+                        "}")
+                .doTest();
+    }
+
+}
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index d083e44..7ad9aec 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -89,6 +89,11 @@
         private @LineBreakWordStyle int mLineBreakWordStyle =
                 LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
 
+        // Whether or not enabling phrase breaking automatically.
+        // TODO(b/226012260): Remove this and add LINE_BREAK_WORD_STYLE_PHRASE_AUTO after
+        // the experiment.
+        private boolean mAutoPhraseBreaking = false;
+
         /**
          * Builder constructor with line break parameters.
          */
@@ -118,12 +123,22 @@
         }
 
         /**
+         * Enable or disable the automation of {@link LINE_BREAK_WORD_STYLE_PHRASE}.
+         *
+         * @hide
+         */
+        public @NonNull Builder setAutoPhraseBreaking(boolean autoPhraseBreaking) {
+            mAutoPhraseBreaking = autoPhraseBreaking;
+            return this;
+        }
+
+        /**
          * Build the {@link LineBreakConfig}
          *
          * @return the LineBreakConfig instance.
          */
         public @NonNull LineBreakConfig build() {
-            return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle);
+            return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle, mAutoPhraseBreaking);
         }
     }
 
@@ -143,6 +158,23 @@
                 .build();
     }
 
+    /**
+     * Create the LineBreakConfig instance.
+     *
+     * @param lineBreakStyle the line break style for text wrapping.
+     * @param lineBreakWordStyle the line break word style for text wrapping.
+     * @return the {@link LineBreakConfig} instance.     *
+     * @hide
+     */
+    public static @NonNull LineBreakConfig getLineBreakConfig(@LineBreakStyle int lineBreakStyle,
+            @LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) {
+        LineBreakConfig.Builder builder = new LineBreakConfig.Builder();
+        return builder.setLineBreakStyle(lineBreakStyle)
+                .setLineBreakWordStyle(lineBreakWordStyle)
+                .setAutoPhraseBreaking(autoPhraseBreaking)
+                .build();
+    }
+
     /** @hide */
     public static final LineBreakConfig NONE =
             new Builder().setLineBreakStyle(LINE_BREAK_STYLE_NONE)
@@ -150,15 +182,17 @@
 
     private final @LineBreakStyle int mLineBreakStyle;
     private final @LineBreakWordStyle int mLineBreakWordStyle;
+    private final boolean mAutoPhraseBreaking;
 
     /**
      * Constructor with the line break parameters.
      * Use the {@link LineBreakConfig.Builder} to create the LineBreakConfig instance.
      */
     private LineBreakConfig(@LineBreakStyle int lineBreakStyle,
-            @LineBreakWordStyle int lineBreakWordStyle) {
+            @LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) {
         mLineBreakStyle = lineBreakStyle;
         mLineBreakWordStyle = lineBreakWordStyle;
+        mAutoPhraseBreaking = autoPhraseBreaking;
     }
 
     /**
@@ -179,6 +213,17 @@
         return mLineBreakWordStyle;
     }
 
+    /**
+     * Used to identify if the automation of {@link LINE_BREAK_WORD_STYLE_PHRASE} is enabled.
+     *
+     * @return The result that records whether or not the automation of
+     * {@link LINE_BREAK_WORD_STYLE_PHRASE} is enabled.
+     * @hide
+     */
+    public boolean getAutoPhraseBreaking() {
+        return mAutoPhraseBreaking;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (o == null) return false;
@@ -186,7 +231,8 @@
         if (!(o instanceof LineBreakConfig)) return false;
         LineBreakConfig that = (LineBreakConfig) o;
         return (mLineBreakStyle == that.mLineBreakStyle)
-                && (mLineBreakWordStyle == that.mLineBreakWordStyle);
+                && (mLineBreakWordStyle == that.mLineBreakWordStyle)
+                && (mAutoPhraseBreaking == that.mAutoPhraseBreaking);
     }
 
     @Override
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 8e01105..c591c87 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -38,8 +38,9 @@
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 
 import javax.crypto.BadPaddingException;
@@ -227,7 +228,7 @@
                 throw new RuntimeException("Error decoding certificates", e);
             }
 
-            LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+            ArrayList<X509Certificate> x509Certs = new ArrayList<>();
             for (Certificate cert : certs) {
                 x509Certs.add((X509Certificate) cert);
             }
@@ -384,7 +385,7 @@
     public @NonNull Collection<X509Certificate> getAuthKeysNeedingCertification() {
         try {
             AuthKeyParcel[] authKeyParcels = mBinder.getAuthKeysNeedingCertification();
-            LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+            ArrayList<X509Certificate> x509Certs = new ArrayList<>();
             CertificateFactory factory = CertificateFactory.getInstance("X.509");
             for (AuthKeyParcel authKeyParcel : authKeyParcels) {
                 Collection<? extends Certificate> certs = null;
diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
index d2e7984..1ad70ed 100644
--- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
@@ -25,8 +25,9 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.LinkedList;
+import java.util.List;
 
 class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
 
@@ -61,7 +62,7 @@
                 throw new RuntimeException("Error decoding certificates", e);
             }
 
-            LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+            ArrayList<X509Certificate> x509Certs = new ArrayList<>();
             for (Certificate cert : certs) {
                 x509Certs.add((X509Certificate) cert);
             }
diff --git a/identity/java/android/security/identity/PersonalizationData.java b/identity/java/android/security/identity/PersonalizationData.java
index b34f250..bdb00fdf 100644
--- a/identity/java/android/security/identity/PersonalizationData.java
+++ b/identity/java/android/security/identity/PersonalizationData.java
@@ -18,10 +18,11 @@
 
 import android.annotation.NonNull;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashMap;
-import java.util.LinkedList;
+import java.util.List;
 
 /**
  * An object that holds personalization data.
@@ -38,7 +39,7 @@
     private PersonalizationData() {
     }
 
-    private LinkedList<AccessControlProfile> mProfiles = new LinkedList<>();
+    private ArrayList<AccessControlProfile> mProfiles = new ArrayList<>();
 
     private LinkedHashMap<String, NamespaceData> mNamespaces = new LinkedHashMap<>();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 31f0ef0..b20caf4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -439,6 +439,7 @@
         }
         notifyLocusVisibilityIfNeeded(info.getTaskInfo());
         notifyCompatUI(info.getTaskInfo(), listener);
+        mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskAdded(info.getTaskInfo()));
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
index 6e78fcb..b71cc32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.recents;
 
+import android.app.ActivityManager;
+
 import com.android.wm.shell.recents.IRecentTasksListener;
 import com.android.wm.shell.util.GroupedRecentTaskInfo;
 
@@ -38,4 +40,9 @@
      * Gets the set of recent tasks.
      */
     GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) = 3;
+
+    /**
+     * Gets the set of running tasks.
+     */
+    ActivityManager.RunningTaskInfo[] getRunningTasks(int maxNum) = 4;
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index 8efa428..59f7233 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.recents;
 
+import android.app.ActivityManager;
+
 /**
  * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
  */
@@ -25,4 +27,14 @@
      * Called when the set of recent tasks change.
      */
     void onRecentTasksChanged();
+
+    /**
+     * Called when a running task appears.
+     */
+    void onRunningTaskAppeared(in ActivityManager.RunningTaskInfo taskInfo);
+
+    /**
+     * Called when a running task vanishes.
+     */
+    void onRunningTaskVanished(in ActivityManager.RunningTaskInfo taskInfo);
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index c166178..d903d5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.recents;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.content.pm.PackageManager.FEATURE_PC;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 
@@ -63,8 +64,9 @@
     private final ShellExecutor mMainExecutor;
     private final TaskStackListenerImpl mTaskStackListener;
     private final RecentTasks mImpl = new RecentTasksImpl();
+    private IRecentTasksListener mListener;
+    private final boolean mIsDesktopMode;
 
-    private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
     // Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a
     // pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
     private final SparseIntArray mSplitTasks = new SparseIntArray();
@@ -95,6 +97,7 @@
     RecentTasksController(Context context, TaskStackListenerImpl taskStackListener,
             ShellExecutor mainExecutor) {
         mContext = context;
+        mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
         mTaskStackListener = taskStackListener;
         mMainExecutor = mainExecutor;
     }
@@ -176,10 +179,15 @@
         notifyRecentTasksChanged();
     }
 
-    public void onTaskRemoved(TaskInfo taskInfo) {
+    public void onTaskAdded(ActivityManager.RunningTaskInfo taskInfo) {
+        notifyRunningTaskAppeared(taskInfo);
+    }
+
+    public void onTaskRemoved(ActivityManager.RunningTaskInfo taskInfo) {
         // Remove any split pairs associated with this task
         removeSplitPair(taskInfo.taskId);
         notifyRecentTasksChanged();
+        notifyRunningTaskVanished(taskInfo);
     }
 
     public void onTaskWindowingModeChanged(TaskInfo taskInfo) {
@@ -189,19 +197,50 @@
     @VisibleForTesting
     void notifyRecentTasksChanged() {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed");
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).run();
+        if (mListener == null) {
+            return;
+        }
+        try {
+            mListener.onRecentTasksChanged();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed call notifyRecentTasksChanged", e);
         }
     }
 
-    private void registerRecentTasksListener(Runnable listener) {
-        if (!mCallbacks.contains(listener)) {
-            mCallbacks.add(listener);
+    /**
+     * Notify the running task listener that a task appeared on desktop environment.
+     */
+    private void notifyRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
+        if (mListener == null || !mIsDesktopMode || taskInfo.realActivity == null) {
+            return;
+        }
+        try {
+            mListener.onRunningTaskAppeared(taskInfo);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed call onRunningTaskAppeared", e);
         }
     }
 
-    private void unregisterRecentTasksListener(Runnable listener) {
-        mCallbacks.remove(listener);
+    /**
+     * Notify the running task listener that a task was removed on desktop environment.
+     */
+    private void notifyRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+        if (mListener == null || !mIsDesktopMode || taskInfo.realActivity == null) {
+            return;
+        }
+        try {
+            mListener.onRunningTaskVanished(taskInfo);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed call onRunningTaskVanished", e);
+        }
+    }
+
+    private void registerRecentTasksListener(IRecentTasksListener listener) {
+        mListener = listener;
+    }
+
+    private void unregisterRecentTasksListener() {
+        mListener = null;
     }
 
     @VisibleForTesting
@@ -280,19 +319,28 @@
         private RecentTasksController mController;
         private final SingleInstanceRemoteListener<RecentTasksController,
                 IRecentTasksListener> mListener;
-        private final Runnable mRecentTasksListener =
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        mListener.call(l -> l.onRecentTasksChanged());
-                    }
-                };
+        private final IRecentTasksListener mRecentTasksListener = new IRecentTasksListener.Stub() {
+            @Override
+            public void onRecentTasksChanged() throws RemoteException {
+                mListener.call(l -> l.onRecentTasksChanged());
+            }
+
+            @Override
+            public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
+                mListener.call(l -> l.onRunningTaskAppeared(taskInfo));
+            }
+
+            @Override
+            public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+                mListener.call(l -> l.onRunningTaskVanished(taskInfo));
+            }
+        };
 
         public IRecentTasksImpl(RecentTasksController controller) {
             mController = controller;
             mListener = new SingleInstanceRemoteListener<>(controller,
                     c -> c.registerRecentTasksListener(mRecentTasksListener),
-                    c -> c.unregisterRecentTasksListener(mRecentTasksListener));
+                    c -> c.unregisterRecentTasksListener());
         }
 
         /**
@@ -331,5 +379,16 @@
                     true /* blocking */);
             return out[0];
         }
+
+        @Override
+        public ActivityManager.RunningTaskInfo[] getRunningTasks(int maxNum) {
+            final ActivityManager.RunningTaskInfo[][] tasks =
+                    new ActivityManager.RunningTaskInfo[][] {null};
+            executeRemoteCallWithTaskPermission(mController, "getRunningTasks",
+                    (controller) -> tasks[0] = ActivityTaskManager.getInstance().getTasks(maxNum)
+                            .toArray(new ActivityManager.RunningTaskInfo[0]),
+                    true /* blocking */);
+            return tasks[0];
+        }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 50f6bd7..9ef8c32 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -30,11 +30,13 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import static java.lang.Integer.MAX_VALUE;
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 
@@ -77,6 +79,7 @@
     @Before
     public void setUp() {
         mMainExecutor = new TestShellExecutor();
+        when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
         mRecentTasksController = spy(new RecentTasksController(mContext, mTaskStackListener,
                 mMainExecutor));
         mShellTaskOrganizer = new ShellTaskOrganizer(mMainExecutor, mContext,
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index c80fb18..8a379d5 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -33,6 +33,7 @@
 
 cc_defaults {
     name: "libandroidfw_defaults",
+    cpp_std: "gnu++2b",
     cflags: [
         "-Werror",
         "-Wunreachable-code",
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index 921877dc..fac2fa4 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -288,12 +288,12 @@
 
 template <typename TChar>
 inline bool operator==(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
-  return rhs == lhs;
+  return BasicStringPiece<TChar>(lhs) == rhs;
 }
 
 template <typename TChar>
 inline bool operator!=(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
-  return rhs != lhs;
+  return BasicStringPiece<TChar>(lhs) != rhs;
 }
 
 }  // namespace android
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index d5fee3f..2e6e36a 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -16,7 +16,9 @@
 #ifndef DEVICEINFO_H
 #define DEVICEINFO_H
 
+#include <SkColorSpace.h>
 #include <SkImageInfo.h>
+#include <SkRefCnt.h>
 #include <android/data_space.h>
 
 #include <mutex>
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index c24cabb..7291cab 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -22,8 +22,11 @@
 #include <GLES2/gl2ext.h>
 #include <GLES3/gl3.h>
 #include <GrDirectContext.h>
+#include <SkBitmap.h>
 #include <SkCanvas.h>
 #include <SkImage.h>
+#include <SkImageInfo.h>
+#include <SkRefCnt.h>
 #include <gui/TraceUtils.h>
 #include <utils/GLUtils.h>
 #include <utils/NdkUtils.h>
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index 81057a2..00ee996 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -17,6 +17,9 @@
 #pragma once
 
 #include <hwui/Bitmap.h>
+#include <SkRefCnt.h>
+
+class SkBitmap;
 
 namespace android::uirenderer {
 
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 4cce87a..79953aa 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -26,6 +26,18 @@
 #include "pipeline/skia/LayerDrawable.h"
 #include "renderthread/EglManager.h"
 #include "renderthread/VulkanManager.h"
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColorSpace.h>
+#include <SkImage.h>
+#include <SkImageInfo.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkSamplingOptions.h>
+#include <SkSurface.h>
 #include "utils/Color.h"
 #include "utils/MathUtils.h"
 #include "utils/NdkUtils.h"
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index d0d748f..aa6e43c 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -20,7 +20,11 @@
 #include "Rect.h"
 #include "renderthread/RenderThread.h"
 
-#include <SkBitmap.h>
+#include <SkRefCnt.h>
+
+class SkBitmap;
+class SkImage;
+struct SkRect;
 
 namespace android {
 class Bitmap;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index a285462..f5ebfd5 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -29,10 +29,14 @@
 #include "SkDrawShadowInfo.h"
 #include "SkImage.h"
 #include "SkImageFilter.h"
+#include "SkImageInfo.h"
 #include "SkLatticeIter.h"
 #include "SkMath.h"
+#include "SkPaint.h"
 #include "SkPicture.h"
+#include "SkRRect.h"
 #include "SkRSXform.h"
+#include "SkRect.h"
 #include "SkRegion.h"
 #include "SkTextBlob.h"
 #include "SkVertices.h"
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 212b4e7..35bec93 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -34,6 +34,8 @@
 #include <SkRuntimeEffect.h>
 #include <vector>
 
+class SkRRect;
+
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 53c6db0..023d6bf 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -27,6 +27,7 @@
 
 #include <SkAndroidFrameworkUtils.h>
 #include <SkAnimatedImage.h>
+#include <SkBitmap.h>
 #include <SkCanvasPriv.h>
 #include <SkCanvasStateUtils.h>
 #include <SkColorFilter.h>
@@ -36,8 +37,13 @@
 #include <SkGraphics.h>
 #include <SkImage.h>
 #include <SkImagePriv.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
 #include <SkPicture.h>
 #include <SkRSXform.h>
+#include <SkRRect.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
 #include <SkShader.h>
 #include <SkTemplates.h>
 #include <SkTextBlob.h>
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 715007c..c6313f6 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -33,6 +33,8 @@
 #include <cassert>
 #include <optional>
 
+class SkRRect;
+
 namespace android {
 
 // Holds an SkCanvas reference plus additional native data.
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 983c776..536ff78 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -21,9 +21,10 @@
 #include <utils/Log.h>
 
 #include "PathParser.h"
-#include "SkColorFilter.h"
+#include "SkImage.h"
 #include "SkImageInfo.h"
-#include "SkShader.h"
+#include "SkSamplingOptions.h"
+#include "SkScalar.h"
 #include "hwui/Paint.h"
 
 #ifdef __ANDROID__
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 30bb04a..c92654c 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -31,6 +31,7 @@
 #include <SkPath.h>
 #include <SkPathMeasure.h>
 #include <SkRect.h>
+#include <SkRefCnt.h>
 #include <SkShader.h>
 #include <SkSurface.h>
 
diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp
index bc6bc45..c442a7b 100644
--- a/libs/hwui/apex/android_bitmap.cpp
+++ b/libs/hwui/apex/android_bitmap.cpp
@@ -24,6 +24,11 @@
 
 #include <GraphicsJNI.h>
 #include <hwui/Bitmap.h>
+#include <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkImageInfo.h>
+#include <SkRefCnt.h>
+#include <SkStream.h>
 #include <utils/Color.h>
 
 using namespace android;
diff --git a/libs/hwui/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp
index 2a939ef..905b123 100644
--- a/libs/hwui/apex/android_canvas.cpp
+++ b/libs/hwui/apex/android_canvas.cpp
@@ -23,7 +23,9 @@
 #include <utils/Color.h>
 
 #include <SkBitmap.h>
+#include <SkColorSpace.h>
 #include <SkSurface.h>
+#include <SkRefCnt.h>
 
 using namespace android;
 
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index fdc97a4..2dcbca8 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -17,13 +17,19 @@
 #pragma once
 
 #include <SkAndroidFrameworkUtils.h>
+#include <SkBlendMode.h>
 #include <SkCanvas.h>
-#include <SkPath.h>
-#include <SkRegion.h>
-#include <SkVertices.h>
+#include <SkClipOp.h>
 #include <SkImage.h>
+#include <SkPaint.h>
+#include <SkPath.h>
 #include <SkPicture.h>
+#include <SkRRect.h>
+#include <SkRect.h>
+#include <SkRegion.h>
 #include <SkRuntimeEffect.h>
+#include <SkSamplingOptions.h>
+#include <SkVertices.h>
 
 #include <log/log.h>
 
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 67f4758..feafc23 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -35,9 +35,15 @@
 #endif
 
 #include <SkCanvas.h>
-#include <SkImagePriv.h>
-#include <SkWebpEncoder.h>
+#include <SkColor.h>
+#include <SkEncodedImageFormat.h>
 #include <SkHighContrastFilter.h>
+#include <SkImageEncoder.h>
+#include <SkImagePriv.h>
+#include <SkPixmap.h>
+#include <SkRect.h>
+#include <SkStream.h>
+#include <SkWebpEncoder.h>
 #include <limits>
 
 namespace android {
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 94a047c..133f1fe 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -19,9 +19,9 @@
 #include <SkColorFilter.h>
 #include <SkColorSpace.h>
 #include <SkImage.h>
-#include <SkImage.h>
 #include <SkImageInfo.h>
 #include <SkPixelRef.h>
+#include <SkRefCnt.h>
 #include <cutils/compiler.h>
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
 #include <android/hardware_buffer.h>
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index 270d24a..d4b0198 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "BlurDrawLooper.h"
+#include <SkColorSpace.h>
 #include <SkMaskFilter.h>
 
 namespace android {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index b046f45..cd8af3d 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -26,6 +26,7 @@
 #include "hwui/PaintFilter.h"
 
 #include <SkFontMetrics.h>
+#include <SkRRect.h>
 
 namespace android {
 
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 8277764..7378351 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -31,6 +31,7 @@
 
 class SkAnimatedImage;
 class SkCanvasState;
+class SkRRect;
 class SkRuntimeShaderBuilder;
 class SkVertices;
 
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index cef2233..b6d73b3 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -17,9 +17,11 @@
 
 #include <SkAndroidCodec.h>
 #include <SkCodec.h>
+#include <SkColorSpace.h>
 #include <SkImageInfo.h>
 #include <SkPngChunkReader.h>
 #include <SkRect.h>
+#include <SkRefCnt.h>
 #include <SkSize.h>
 #include <cutils/compiler.h>
 
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 2db3ace..34cb4ae 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -16,10 +16,13 @@
 
 #include "MinikinSkia.h"
 
-#include <SkFontDescriptor.h>
 #include <SkFont.h>
+#include <SkFontDescriptor.h>
 #include <SkFontMetrics.h>
 #include <SkFontMgr.h>
+#include <SkRect.h>
+#include <SkScalar.h>
+#include <SkStream.h>
 #include <SkTypeface.h>
 #include <log/log.h>
 
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index c40b858..373e893 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -21,8 +21,11 @@
 #include <SkAndroidCodec.h>
 #include <SkAnimatedImage.h>
 #include <SkColorFilter.h>
+#include <SkEncodedImageFormat.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
 #include <hwui/AnimatedImageDrawable.h>
 #include <hwui/ImageDecoder.h>
 #include <hwui/Canvas.h>
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 5db0783..94cea65 100755
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -2,17 +2,25 @@
 #define LOG_TAG "Bitmap"
 #include "Bitmap.h"
 
+#include "GraphicsJNI.h"
 #include "SkBitmap.h"
+#include "SkBlendMode.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
 #include "SkColorSpace.h"
-#include "SkPixelRef.h"
+#include "SkData.h"
 #include "SkImageEncoder.h"
 #include "SkImageInfo.h"
-#include "GraphicsJNI.h"
+#include "SkPaint.h"
+#include "SkPixelRef.h"
+#include "SkPixmap.h"
+#include "SkPoint.h"
+#include "SkRefCnt.h"
 #include "SkStream.h"
+#include "SkTypes.h"
 #include "SkWebpEncoder.h"
 
+
 #include "android_nio_utils.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 #include <hwui/Paint.h>
diff --git a/libs/hwui/jni/Bitmap.h b/libs/hwui/jni/Bitmap.h
index 73eca3a..21a93f0 100644
--- a/libs/hwui/jni/Bitmap.h
+++ b/libs/hwui/jni/Bitmap.h
@@ -19,7 +19,6 @@
 #include <jni.h>
 #include <android/bitmap.h>
 
-class SkBitmap;
 struct SkImageInfo;
 
 namespace android {
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index 4e9daa4..320d332 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -8,9 +8,19 @@
 #include "MimeType.h"
 #include "NinePatchPeeker.h"
 #include "SkAndroidCodec.h"
+#include "SkBitmap.h"
+#include "SkBlendMode.h"
 #include "SkCanvas.h"
+#include "SkColorSpace.h"
+#include "SkEncodedImageFormat.h"
+#include "SkImageInfo.h"
 #include "SkMath.h"
+#include "SkPaint.h"
 #include "SkPixelRef.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+#include "SkSamplingOptions.h"
+#include "SkSize.h"
 #include "SkStream.h"
 #include "SkString.h"
 #include "SkUtils.h"
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index 1c20415..eb56ae3 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -25,6 +25,7 @@
 #include "BitmapRegionDecoder.h"
 #include "SkBitmap.h"
 #include "SkCodec.h"
+#include "SkColorSpace.h"
 #include "SkData.h"
 #include "SkStream.h"
 
diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
index b10540c..97dbc9a 100644
--- a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
+++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
@@ -2,6 +2,7 @@
 #include "GraphicsJNI.h"
 #include "Utils.h"
 
+#include <SkData.h>
 #include <SkStream.h>
 
 using namespace android;
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index ce5ac38..acc1b04 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -24,6 +24,7 @@
 #include "SkData.h"
 #include "SkFontMgr.h"
 #include "SkRefCnt.h"
+#include "SkStream.h"
 #include "SkTypeface.h"
 #include "Utils.h"
 #include "fonts/Font.h"
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index fef51b8..ae6ac4c 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -7,9 +7,11 @@
 
 
 #include "Movie.h"
+#include "SkBitmap.h"
 #include "SkColor.h"
 #include "SkColorPriv.h"
 #include "SkStream.h"
+#include "SkTypes.h"
 
 #include "gif_lib.h"
 
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 33669ac..6a3bc8f 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -8,10 +8,18 @@
 #include <nativehelper/JNIHelp.h>
 #include "GraphicsJNI.h"
 
+#include "include/private/SkTemplates.h" // SkTAddOffset
+#include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkColorSpace.h"
 #include "SkFontMetrics.h"
+#include "SkImageInfo.h"
 #include "SkMath.h"
+#include "SkPixelRef.h"
+#include "SkPoint.h"
+#include "SkRect.h"
 #include "SkRegion.h"
+#include "SkTypes.h"
 #include <cutils/ashmem.h>
 #include <hwui/Canvas.h>
 
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index f7b8c01..bad710d 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -29,8 +29,12 @@
 
 #include <FrontBufferedStream.h>
 #include <SkAndroidCodec.h>
-#include <SkEncodedImageFormat.h>
+#include <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkImageInfo.h>
+#include <SkRect.h>
 #include <SkStream.h>
+#include <SkString.h>
 
 #include <androidfw/Asset.h>
 #include <fcntl.h>
diff --git a/libs/hwui/jni/Movie.h b/libs/hwui/jni/Movie.h
index 736890d..02113dd 100644
--- a/libs/hwui/jni/Movie.h
+++ b/libs/hwui/jni/Movie.h
@@ -13,6 +13,7 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkRefCnt.h"
+#include "SkTypes.h"
 
 class SkStreamRewindable;
 
diff --git a/libs/hwui/jni/MovieImpl.cpp b/libs/hwui/jni/MovieImpl.cpp
index ae9e04e..abb75fa 100644
--- a/libs/hwui/jni/MovieImpl.cpp
+++ b/libs/hwui/jni/MovieImpl.cpp
@@ -5,11 +5,12 @@
  * found in the LICENSE file.
  */
 #include "Movie.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
+#include "SkBitmap.h"
+#include "SkStream.h"
+#include "SkTypes.h"
 
 // We should never see this in normal operation since our time values are
-// 0-based. So we use it as a sentinal.
+// 0-based. So we use it as a sentinel.
 #define UNINITIALIZED_MSEC ((SkMSec)-1)
 
 Movie::Movie()
@@ -81,8 +82,6 @@
 
 ////////////////////////////////////////////////////////////////////
 
-#include "SkStream.h"
-
 Movie* Movie::DecodeMemory(const void* data, size_t length) {
     SkMemoryStream stream(data, length, false);
     return Movie::DecodeStream(&stream);
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index 08fc80f..d50a8a2 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -24,8 +24,10 @@
 #include <hwui/Paint.h>
 #include <utils/Log.h>
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkLatticeIter.h"
+#include "SkRect.h"
 #include "SkRegion.h"
 #include "GraphicsJNI.h"
 #include "NinePatchPeeker.h"
diff --git a/libs/hwui/jni/NinePatchPeeker.cpp b/libs/hwui/jni/NinePatchPeeker.cpp
index 9171fc6..d85ede5 100644
--- a/libs/hwui/jni/NinePatchPeeker.cpp
+++ b/libs/hwui/jni/NinePatchPeeker.cpp
@@ -16,7 +16,7 @@
 
 #include "NinePatchPeeker.h"
 
-#include <SkBitmap.h>
+#include <SkScalar.h>
 #include <cutils/compiler.h>
 
 using namespace android;
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index f768632..0aa1465 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -26,6 +26,7 @@
 #include <nativehelper/ScopedPrimitiveArray.h>
 
 #include "SkColorFilter.h"
+#include "SkColorSpace.h"
 #include "SkFont.h"
 #include "SkFontMetrics.h"
 #include "SkFontTypes.h"
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 0bbd8a8..fa8e2e7 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -2,11 +2,21 @@
 #define LOG_TAG "ShaderJNI"
 
 #include "GraphicsJNI.h"
+#include "SkBitmap.h"
+#include "SkBlendMode.h"
+#include "SkColor.h"
 #include "SkColorFilter.h"
 #include "SkGradientShader.h"
+#include "SkImage.h"
 #include "SkImagePriv.h"
+#include "SkMatrix.h"
+#include "SkPoint.h"
+#include "SkRefCnt.h"
+#include "SkSamplingOptions.h"
+#include "SkScalar.h"
 #include "SkShader.h"
-#include "SkBlendMode.h"
+#include "SkString.h"
+#include "SkTileMode.h"
 #include "include/effects/SkRuntimeEffect.h"
 
 #include <vector>
@@ -16,7 +26,7 @@
 /**
  * By default Skia gradients will interpolate their colors in unpremul space
  * and then premultiply each of the results. We must set this flag to preserve
- * backwards compatiblity by premultiplying the colors of the gradient first,
+ * backwards compatibility by premultiplying the colors of the gradient first,
  * and then interpolating between them.
  */
 static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h
index 6cdf44d..f6e3a0e 100644
--- a/libs/hwui/jni/Utils.h
+++ b/libs/hwui/jni/Utils.h
@@ -17,8 +17,11 @@
 #ifndef _ANDROID_GRAPHICS_UTILS_H_
 #define _ANDROID_GRAPHICS_UTILS_H_
 
+#include "SkRefCnt.h"
 #include "SkStream.h"
 
+class SkData;
+
 #include <jni.h>
 #include <androidfw/Asset.h>
 
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 77f42ae..87eda7e 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -1,5 +1,7 @@
 #include "CreateJavaOutputStreamAdaptor.h"
 #include "SkJPEGWriteUtility.h"
+#include "SkStream.h"
+#include "SkTypes.h"
 #include "YuvToJpegEncoder.h"
 #include <ui/PixelFormat.h>
 #include <hardware/hardware.h>
diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h
index 7e7b935..a69726b1 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.h
+++ b/libs/hwui/jni/YuvToJpegEncoder.h
@@ -1,13 +1,13 @@
 #ifndef _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
 #define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
 
-#include "SkTypes.h"
-#include "SkStream.h"
 extern "C" {
     #include "jpeglib.h"
     #include "jerror.h"
 }
 
+class SkWStream;
+
 class YuvToJpegEncoder {
 public:
     /** Create an encoder based on the YUV format.
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 0ef80ee..61bb665 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -32,10 +32,22 @@
 
 #include "FontUtils.h"
 #include "Bitmap.h"
+#include "SkBitmap.h"
+#include "SkBlendMode.h"
+#include "SkClipOp.h"
+#include "SkColor.h"
+#include "SkColorSpace.h"
 #include "SkGraphics.h"
+#include "SkImageInfo.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
 #include "SkRegion.h"
-#include "SkVertices.h"
 #include "SkRRect.h"
+#include "SkScalar.h"
+#include "SkVertices.h"
 
 namespace minikin {
 class MeasuredText;
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index c48448d..55b1f23 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -23,8 +23,16 @@
 #include <Picture.h>
 #include <Properties.h>
 #include <RootRenderNode.h>
+#include <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkData.h>
+#include <SkImage.h>
 #include <SkImagePriv.h>
+#include <SkPicture.h>
+#include <SkPixmap.h>
 #include <SkSerialProcs.h>
+#include <SkStream.h>
+#include <SkTypeface.h>
 #include <dlfcn.h>
 #include <gui/TraceUtils.h>
 #include <inttypes.h>
@@ -451,7 +459,7 @@
 };
 
 // TODO: This & Multi-SKP & Single-SKP should all be de-duped into
-// a single "make a SkPicture serailizable-safe" utility somewhere
+// a single "make a SkPicture serializable-safe" utility somewhere
 class PictureWrapper : public Picture {
 public:
     PictureWrapper(sk_sp<SkPicture>&& src, const std::shared_ptr<PictureCaptureState>& state)
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 09be630..2c421f8 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -22,7 +22,10 @@
 #include "SkFont.h"
 #include "SkFontMetrics.h"
 #include "SkFontMgr.h"
+#include "SkRect.h"
 #include "SkRefCnt.h"
+#include "SkScalar.h"
+#include "SkStream.h"
 #include "SkTypeface.h"
 #include "GraphicsJNI.h"
 #include <nativehelper/ScopedUtfChars.h>
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 3f89c07..6a052db 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -19,6 +19,8 @@
 #include "RenderNode.h"
 #include "SkiaDisplayList.h"
 
+class SkRRect;
+
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
diff --git a/libs/hwui/pipeline/skia/HolePunch.h b/libs/hwui/pipeline/skia/HolePunch.h
index 92c6f77..d0e1ca3 100644
--- a/libs/hwui/pipeline/skia/HolePunch.h
+++ b/libs/hwui/pipeline/skia/HolePunch.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <string>
-#include "SkRRect.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 507d3dc..3bf2b2e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -15,7 +15,11 @@
  */
 
 #include "RenderNodeDrawable.h"
+#include <SkPaint.h>
 #include <SkPaintFilterCanvas.h>
+#include <SkPoint.h>
+#include <SkRRect.h>
+#include <SkRect.h>
 #include <gui/TraceUtils.h>
 #include "RenderNode.h"
 #include "SkiaDisplayList.h"
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index e7432ac..ef3f1ca 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -16,6 +16,7 @@
 
 #include "ShaderCache.h"
 #include <GrDirectContext.h>
+#include <SkData.h>
 #include <gui/TraceUtils.h>
 #include <log/log.h>
 #include <openssl/sha.h>
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 4dcc9fb..dd57d42 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -17,12 +17,15 @@
 #pragma once
 
 #include <GrContextOptions.h>
+#include <SkRefCnt.h>
 #include <cutils/compiler.h>
 #include <memory>
 #include <mutex>
 #include <string>
 #include <vector>
 
+class SkData;
+
 namespace android {
 
 class BlobCache;
@@ -45,7 +48,7 @@
      * and puts the ShaderCache into an initialized state, such that it is
      * able to insert and retrieve entries from the cache. If identity is
      * non-null and validation fails, the cache is initialized but contains
-     * no data. If size is less than zero, the cache is initilaized but
+     * no data. If size is less than zero, the cache is initialized but
      * contains no data.
      *
      * This should be called when HWUI pipeline is initialized. When not in
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index bc386fe..c546ada 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -16,15 +16,25 @@
 
 #include "SkiaPipeline.h"
 
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkColorSpace.h>
+#include <SkData.h>
+#include <SkImage.h>
 #include <SkImageEncoder.h>
 #include <SkImageInfo.h>
 #include <SkImagePriv.h>
+#include <SkMatrix.h>
 #include <SkMultiPictureDocument.h>
 #include <SkOverdrawCanvas.h>
 #include <SkOverdrawColorFilter.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
 #include <SkSerialProcs.h>
+#include <SkStream.h>
+#include <SkString.h>
 #include <SkTypeface.h>
 #include <android-base/properties.h>
 #include <unistd.h>
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index bc8a565..7887d1a 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -16,14 +16,16 @@
 
 #pragma once
 
-#include <SkSurface.h>
+#include <SkColorSpace.h>
 #include <SkDocument.h>
 #include <SkMultiPictureDocument.h>
+#include <SkSurface.h>
 #include "Lighting.h"
 #include "hwui/AnimatedImageDrawable.h"
 #include "renderthread/CanvasContext.h"
 #include "renderthread/IRenderPipeline.h"
 
+class SkFILEWStream;
 class SkPictureRecorder;
 struct SkSharingSerialContext;
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 9c51e62..5c6117d 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -16,7 +16,20 @@
 
 #include "SkiaRecordingCanvas.h"
 #include "hwui/Paint.h"
+#include <include/private/SkTemplates.h> // SkAutoSTMalloc
+#include <SkBlendMode.h>
+#include <SkData.h>
+#include <SkDrawable.h>
+#include <SkImage.h>
 #include <SkImagePriv.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkRRect.h>
+#include <SkSamplingOptions.h>
+#include <SkTypes.h>
 #include "CanvasTransform.h"
 #ifdef __ANDROID__ // Layoutlib does not support Layers
 #include "Layer.h"
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 1445a27..89e3a2c 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -22,6 +22,11 @@
 #include "SkiaDisplayList.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 
+class SkBitmap;
+class SkMatrix;
+class SkPaint;
+class SkRRect;
+
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 56d42e0..a721be3 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -22,6 +22,11 @@
 
 #include "renderstate/RenderState.h"
 
+#include "SkRefCnt.h"
+
+class SkBitmap;
+struct SkRect;
+
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 41e3687..33160d0 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -19,6 +19,10 @@
 #include "HolePunch.h"
 #include "SkData.h"
 #include "SkDrawable.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkRect.h"
+#include "SkRRect.h"
 
 using namespace android::uirenderer::skiapipeline;
 
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.h b/libs/hwui/pipeline/skia/TransformCanvas.h
index 685b71d..15f0c1a 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.h
+++ b/libs/hwui/pipeline/skia/TransformCanvas.h
@@ -19,6 +19,13 @@
 #include "SkPaintFilterCanvas.h"
 #include <effects/StretchEffect.h>
 
+class SkData;
+class SkDrawable;
+class SkMatrix;
+class SkPaint;
+enum class SkBlendMode;
+struct SkRect;
+
 class TransformCanvas : public SkPaintFilterCanvas {
 public:
     TransformCanvas(SkCanvas* target, SkBlendMode blendmode) :
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index fc6b28d..b8f8c92 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -18,6 +18,7 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
+#include <SkColorSpace.h>
 #include <SkImageInfo.h>
 #include <SkRect.h>
 #include <cutils/compiler.h>
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index aceb5a5..0238889 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -24,6 +24,7 @@
 #include "hwui/Bitmap.h"
 #include "ColorMode.h"
 
+#include <SkColorSpace.h>
 #include <SkRect.h>
 #include <utils/RefBase.h>
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index a44b498..b2ba15c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -29,6 +29,10 @@
 #include "utils/Macros.h"
 #include "utils/TimeUtils.h"
 
+#include <SkBitmap.h>
+#include <SkImage.h>
+#include <SkPicture.h>
+
 #include <pthread.h>
 
 namespace android {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ee9efd4..bbfeeac 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -17,7 +17,7 @@
 #ifndef RENDERPROXY_H_
 #define RENDERPROXY_H_
 
-#include <SkBitmap.h>
+#include <SkRefCnt.h>
 #include <android/native_window.h>
 #include <cutils/compiler.h>
 #include <android/surface_control.h>
@@ -30,6 +30,10 @@
 #include "SwapBehavior.h"
 #include "hwui/Bitmap.h"
 
+class SkBitmap;
+class SkPicture;
+class SkImage;
+
 namespace android {
 class GraphicBuffer;
 class Surface;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 01b956c..3ff4081 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -266,7 +266,7 @@
     }
     mEglManager->initialize();
 
-    sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+    sk_sp<const GrGLInterface> glInterface = GrGLMakeNativeInterface();
     LOG_ALWAYS_FATAL_IF(!glInterface.get());
 
     GrContextOptions options;
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index b816649..cba210d 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -51,6 +51,9 @@
 #include "VulkanSurface.h"
 #include "private/hwui/DrawVkInfo.h"
 
+#include <SkColorSpace.h>
+#include <SkRefCnt.h>
+
 class GrVkExtensions;
 
 namespace android {
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index beb71b72..2648666 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -20,6 +20,7 @@
 #include <system/window.h>
 #include <vulkan/vulkan.h>
 
+#include <SkColorSpace.h>
 #include <SkRefCnt.h>
 #include <SkSize.h>
 
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 491af43..a4890ed 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -26,7 +26,13 @@
 #include <renderthread/VulkanManager.h>
 #include <utils/Unicode.h>
 
+#include "SkCanvas.h"
 #include "SkColorData.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkPixmap.h"
+#include "SkRect.h"
+#include "SkSurface.h"
 #include "SkUnPreMultiply.h"
 
 namespace android {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 5092675..75865c7 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -27,10 +27,20 @@
 #include <renderstate/RenderState.h>
 #include <renderthread/RenderThread.h>
 
+#include <SkBitmap.h>
+#include <SkColor.h>
+#include <SkImageInfo.h>
+#include <SkRefCnt.h>
+
 #include <gtest/gtest.h>
 #include <memory>
 #include <unordered_map>
 
+class SkCanvas;
+class SkMatrix;
+class SkPath;
+struct SkRect;
+
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index 03aeb55..a07cdf7 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -14,7 +14,17 @@
  * limitations under the License.
  */
 
-#include <SkImagePriv.h>
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkImageInfo.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkSamplingOptions.h>
+#include <SkShader.h>
+#include <SkTileMode.h>
 #include "hwui/Paint.h"
 #include "TestSceneBase.h"
 #include "tests/common/BitmapAllocationTestUtils.h"
diff --git a/libs/hwui/tests/common/scenes/HwBitmap565.cpp b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
index cbdb756..de0ef6d 100644
--- a/libs/hwui/tests/common/scenes/HwBitmap565.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
@@ -18,6 +18,12 @@
 #include "tests/common/BitmapAllocationTestUtils.h"
 #include "utils/Color.h"
 
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkRefCnt.h>
+
 class HwBitmap565;
 
 static TestScene::Registrar _HwBitmap565(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 564354f..0d5ca6d 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -17,6 +17,7 @@
 #include "TestSceneBase.h"
 #include "utils/Color.h"
 
+#include <SkColorSpace.h>
 #include <SkGradientShader.h>
 #include <SkImagePriv.h>
 #include <ui/PixelFormat.h>
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index d031923..4a5d946 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -17,7 +17,16 @@
 #include "TestSceneBase.h"
 #include "tests/common/TestListViewSceneBase.h"
 #include "hwui/Paint.h"
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
 #include <SkFont.h>
+#include <SkFontTypes.h>
+#include <SkPaint.h>
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkScalar.h>
 #include <cstdio>
 
 class ListViewAnimation;
@@ -48,7 +57,7 @@
                 128 * 3;
         paint.setColor(bgDark ? Color::White : Color::Grey_700);
 
-	SkFont font;
+        SkFont font;
         font.setSize(size / 2);
         char charToShow = 'A' + (rand() % 26);
         const SkPoint pos = {SkIntToScalar(size / 2),
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index edadf78..070a339 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -19,6 +19,10 @@
 #include "utils/Color.h"
 #include "hwui/Paint.h"
 
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkFont.h>
+
 class MagnifierAnimation;
 
 static TestScene::Registrar _Magnifier(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
index 716d397..3caaf82 100644
--- a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
+++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
@@ -16,6 +16,12 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+
 class ReadbackFromHardware;
 
 static TestScene::Registrar _SaveLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
index 1c25078..27948f8 100644
--- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
@@ -17,6 +17,11 @@
 #include "TestSceneBase.h"
 #include "utils/Color.h"
 
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkColor.h>
+#include <SkRefCnt.h>
+
 class RecentsAnimation;
 
 static TestScene::Registrar _Recents(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index e677549..59230a7 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -14,7 +14,15 @@
  * limitations under the License.
  */
 
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
 #include <SkFont.h>
+#include <SkFontTypes.h>
+#include <SkPaint.h>
+#include <SkPoint.h>
+#include <SkRefCnt.h>
+#include <SkRRect.h>
 #include <cstdio>
 #include "TestSceneBase.h"
 #include "hwui/Paint.h"
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
index c6219c48..aff8ca1 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
+#include "SkBitmap.h"
 #include "SkBlendMode.h"
+#include "SkColorFilter.h"
+#include "SkFont.h"
+#include "SkImageInfo.h"
+#include "SkRefCnt.h"
 #include "TestSceneBase.h"
 #include "tests/common/BitmapAllocationTestUtils.h"
 #include "hwui/Paint.h"
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index 2cf3456..d2b1ef9 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -23,9 +23,17 @@
 
 #include <tests/common/CallCountingCanvas.h>
 
-#include "SkPictureRecorder.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
 #include "SkColor.h"
+#include "SkImageInfo.h"
 #include "SkLatticeIter.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPictureRecorder.h"
+#include "SkRRect.h"
+#include "SkRect.h"
+#include "SkRegion.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 #include <SkNoDrawCanvas.h>
 
diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp
index 7f2e158..ec9ab90 100644
--- a/libs/hwui/tests/unit/EglManagerTests.cpp
+++ b/libs/hwui/tests/unit/EglManagerTests.cpp
@@ -20,6 +20,8 @@
 #include "renderthread/RenderEffectCapabilityQuery.h"
 #include "tests/common/TestContext.h"
 
+#include <SkColorSpace.h>
+
 using namespace android;
 using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 2a74afc..96a0c61 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -19,6 +19,8 @@
 #include <SkCanvas.h>
 #include <gtest/gtest.h>
 
+class SkRRect;
+
 namespace {
 
 class TestCanvasBase : public SkCanvas {
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 87981f1..105fea4 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -25,6 +25,8 @@
 #include <cstdint>
 #include "FileBlobCache.h"
 #include "pipeline/skia/ShaderCache.h"
+#include <SkData.h>
+#include <SkRefCnt.h>
 
 using namespace android::uirenderer::skiapipeline;
 
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index dc1b2e6..c1ddbd36 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -16,9 +16,14 @@
 
 #include "tests/common/TestUtils.h"
 
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkColor.h>
 #include <SkColorMatrixFilter.h>
 #include <SkColorSpace.h>
-#include <SkImagePriv.h>
+#include <SkImageInfo.h>
+#include <SkPaint.h>
+#include <SkPath.h>
 #include <SkPathOps.h>
 #include <SkShader.h>
 #include <gtest/gtest.h>
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index dae3c94..50d9f56 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -18,6 +18,7 @@
 
 #include <hwui/Paint.h>
 #include <SkCanvasStateUtils.h>
+#include <SkColorSpace.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
 #include <gtest/gtest.h>
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index 15ecf58..ced667e 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -17,6 +17,7 @@
 #include <VectorDrawable.h>
 #include <gtest/gtest.h>
 
+#include <SkCanvas.h>
 #include <SkClipStack.h>
 #include <SkSurface_Base.h>
 #include <string.h>
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index ab23448..9295a93 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -21,8 +21,11 @@
 #include <sys/stat.h>
 #include <utils/Log.h>
 
+#include "SkData.h"
 #include "SkFontMgr.h"
+#include "SkRefCnt.h"
 #include "SkStream.h"
+#include "SkTypeface.h"
 
 #include "hwui/MinikinSkia.h"
 #include "hwui/Typeface.h"
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 6d4c574..c1c21bd 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -21,6 +21,12 @@
 #include "utils/MathUtils.h"
 #include "utils/VectorDrawableUtils.h"
 
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPath.h>
+#include <SkRefCnt.h>
+#include <SkShader.h>
+
 #include <functional>
 
 namespace android {
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 45da008..956101e 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -24,12 +24,6 @@
 
 #include <log/log.h>
 
-#include <SkBitmap.h>
-#include <SkBlendMode.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-
 namespace {
 // Time to spend fading out the pointer completely.
 const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
index f7c685f..4ac66c4 100644
--- a/libs/input/TouchSpotController.cpp
+++ b/libs/input/TouchSpotController.cpp
@@ -23,12 +23,6 @@
 
 #include <log/log.h>
 
-#include <SkBitmap.h>
-#include <SkBlendMode.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-
 namespace {
 // Time to spend fading out the spot completely.
 const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
diff --git a/media/aidl/android/media/audio/common/AudioPort.aidl b/media/aidl/android/media/audio/common/AudioPort.aidl
index 84675e3..d32b840 100644
--- a/media/aidl/android/media/audio/common/AudioPort.aidl
+++ b/media/aidl/android/media/audio/common/AudioPort.aidl
@@ -23,8 +23,9 @@
 import android.media.audio.common.ExtraAudioDescriptor;
 
 /**
- * Audio port structure describes the capabilities of an audio port
- * as well as its current configuration.
+ * Audio port structure describes the capabilities of an audio port.
+ * This is a "blueprint" which contains all the possible configurations
+ * that are supported by the port.
  *
  * {@hide}
  */
diff --git a/media/aidl/android/media/audio/common/AudioPortExt.aidl b/media/aidl/android/media/audio/common/AudioPortExt.aidl
index c4681cb..eadc0ab 100644
--- a/media/aidl/android/media/audio/common/AudioPortExt.aidl
+++ b/media/aidl/android/media/audio/common/AudioPortExt.aidl
@@ -34,6 +34,9 @@
     AudioPortDeviceExt device;
     /** Information specific to mix ports. */
     AudioPortMixExt mix;
-    /** Audio session identifier. */
+    /**
+     * NOT USED. Framework audio session identifier.
+     * Use android.media.AudioPortExtSys.session on the system side.
+     */
     int session;
 }
diff --git a/media/aidl/android/media/audio/common/AudioPortMixExt.aidl b/media/aidl/android/media/audio/common/AudioPortMixExt.aidl
index f3613a4..eb117ec 100644
--- a/media/aidl/android/media/audio/common/AudioPortMixExt.aidl
+++ b/media/aidl/android/media/audio/common/AudioPortMixExt.aidl
@@ -32,12 +32,12 @@
     AudioPortMixExtUseCase usecase;
     /**
      * Maximum number of input or output streams that can be simultaneously
-     * opened for this port.
+     * opened for this port. '0' means 'unlimited'.
      */
     int maxOpenStreamCount;
     /**
      * Maximum number of input or output streams that can be simultaneously
-     * active for this port.
+     * active for this port. '0' means 'all opened streams'.
      */
     int maxActiveStreamCount;
     /** Mute duration while changing device, when used for output. */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e7eda3e..2c68273 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1636,8 +1636,10 @@
      *
      * @param on set <var>true</var> to turn on speakerphone;
      *           <var>false</var> to turn it off
+     * @deprecated Use {@link AudioManager#setCommunicationDevice(AudioDeviceInfo)} or
+     *           {@link AudioManager#clearCommunicationDevice()} instead.
      */
-    public void setSpeakerphoneOn(boolean on){
+    @Deprecated public void setSpeakerphoneOn(boolean on) {
         final IAudioService service = getService();
         try {
             service.setSpeakerphoneOn(mICallBack, on);
@@ -1650,8 +1652,9 @@
      * Checks whether the speakerphone is on or off.
      *
      * @return true if speakerphone is on, false if it's off
+     * @deprecated Use {@link AudioManager#getCommunicationDevice()} instead.
      */
-    public boolean isSpeakerphoneOn() {
+    @Deprecated public boolean isSpeakerphoneOn() {
         final IAudioService service = getService();
         try {
             return service.isSpeakerphoneOn();
@@ -2717,8 +2720,9 @@
      * connection is established.
      * @see #stopBluetoothSco()
      * @see #ACTION_SCO_AUDIO_STATE_UPDATED
+     * @deprecated Use {@link AudioManager#setCommunicationDevice(AudioDeviceInfo)} instead.
      */
-    public void startBluetoothSco(){
+    @Deprecated public void startBluetoothSco() {
         final IAudioService service = getService();
         try {
             service.startBluetoothSco(mICallBack,
@@ -2761,9 +2765,10 @@
      * bluetooth SCO audio with {@link #startBluetoothSco()} when finished with the SCO
      * connection or if connection fails.
      * @see #startBluetoothSco()
+     * @deprecated Use {@link AudioManager#clearCommunicationDevice()} instead.
      */
     // Also used for connections started with {@link #startBluetoothScoVirtualCall()}
-    public void stopBluetoothSco(){
+    @Deprecated public void stopBluetoothSco() {
         final IAudioService service = getService();
         try {
             service.stopBluetoothSco(mICallBack);
@@ -2795,8 +2800,9 @@
      *
      * @return true if SCO is used for communications;
      *         false if otherwise
+     * @deprecated Use {@link AudioManager#getCommunicationDevice()} instead.
      */
-    public boolean isBluetoothScoOn() {
+    @Deprecated public boolean isBluetoothScoOn() {
         final IAudioService service = getService();
         try {
             return service.isBluetoothScoOn();
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 149c2f4..04d28e7 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -60,7 +60,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -484,7 +483,7 @@
     private final Object mLock = new Object();
 
     // @GuardedBy("mLock")
-    private final List<TvInputCallbackRecord> mCallbackRecords = new LinkedList<>();
+    private final List<TvInputCallbackRecord> mCallbackRecords = new ArrayList<>();
 
     // A mapping from TV input ID to the state of corresponding input.
     // @GuardedBy("mLock")
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 43d4605..88ca30c 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -50,8 +50,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -302,7 +302,7 @@
             new SparseArray<>();
 
     // @GuardedBy("mLock")
-    private final List<TvInteractiveAppCallbackRecord> mCallbackRecords = new LinkedList<>();
+    private final List<TvInteractiveAppCallbackRecord> mCallbackRecords = new ArrayList<>();
 
     // A sequence number for the next session to be created. Should be protected by a lock
     // {@code mSessionCallbackRecordMap}.
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 18b779f..e3e200f 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -129,6 +129,7 @@
 cc_library_shared {
     name: "libmedia_jni_utils",
     srcs: [
+        ":libgui_frame_event_aidl",
         "android_media_Utils.cpp",
     ],
 
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index bb25274..cd6ed23 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -25,6 +25,12 @@
 #include <hwui/ImageDecoder.h>
 #include <log/log.h>
 #include <SkAndroidCodec.h>
+#include <SkCodec.h>
+#include <SkColorSpace.h>
+#include <SkImageInfo.h>
+#include <SkRect.h>
+#include <SkSize.h>
+#include <SkStream.h>
 #include <utils/Color.h>
 
 #include <fcntl.h>
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 8b5d214..c1fce7c 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -48,6 +48,14 @@
             android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
             android:theme="@style/ChooserActivity"/>
 
+        <activity
+            android:name=".CompanionDeviceDataTransferActivity"
+            android:exported="true"
+            android:launchMode="singleInstance"
+            android:excludeFromRecents="true"
+            android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
+            android:theme="@style/ChooserActivity"/>
+
         <service
             android:name=".CompanionDeviceDiscoveryService"
             android:exported="false" />
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 586a022..fca9ab9 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -107,13 +107,11 @@
     <string name="consent_back">Back</string>
 
     <!-- ================== System data transfer ==================== -->
-    <!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=60] -->
-    <string name="permission_sync_confirmation_title">Transfer app permissions to your
-        watch</string>
+    <!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=NONE] -->
+    <string name="permission_sync_confirmation_title">Give apps on &lt;strong&gt;<xliff:g id="companion_device_name" example="Galaxy Watch 5">%1$s</xliff:g>&lt;/strong&gt; the same permissions as on &lt;strong&gt;<xliff:g id="primary_device_name" example="Pixel 6">%2$s</xliff:g>&lt;/strong&gt;?</string>
 
-    <!-- Text of the permission sync explanation in the confirmation dialog. [CHAR LIMIT=400] -->
-    <string name="permission_sync_summary">To make it easier to set up your watch,
-        apps installed on your watch during setup will use the same permissions as your phone.\n\n
-        These permissions may include access to your watch\u2019s microphone and location.</string>
+    <!-- Text of the permission sync explanation in the confirmation dialog. [CHAR LIMIT=NONE] -->
+    <string name="permission_sync_summary">&lt;p&gt;This may include Microphone, Camera, and Location access, and other sensitive permissions on &lt;strong&gt;<xliff:g id="companion_device_name" example="Galaxy Watch 5">%1$s</xliff:g>&lt;/strong&gt;.&lt;/p&gt;
+        &lt;p&gt;You can change these permissions any time in your Settings on &lt;strong&gt;<xliff:g id="companion_device_name" example="Galaxy Watch 5">%1$s</xliff:g>&lt;/strong&gt;.&lt;/p&gt;</string>
 
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index c38323f..809e98e 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -49,7 +49,6 @@
     <style name="DescriptionSummary">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
-        <item name="android:gravity">center</item>
         <item name="android:layout_marginTop">18dp</item>
         <item name="android:layout_marginLeft">18dp</item>
         <item name="android:layout_marginRight">18dp</item>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 37cbf30..e71c945 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -307,6 +307,7 @@
 
     private void onUserSelectedDevice(@NonNull DeviceFilterPair<?> selectedDevice) {
         final MacAddress macAddress = selectedDevice.getMacAddress();
+        mRequest.setDisplayName(selectedDevice.getDisplayName());
         onAssociationApproved(macAddress);
     }
 
@@ -486,6 +487,7 @@
         mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
 
         final String deviceName = mSelectedDevice.getDisplayName();
+        mRequest.setDisplayName(deviceName);
         final Spanned title = getHtmlFromResources(
                 this, R.string.confirmation_title, appLabel, deviceName);
         final Spanned summary;
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
index 67efa03..93040b5 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
@@ -16,20 +16,22 @@
 
 package com.android.companiondevicemanager;
 
+import static android.companion.datatransfer.SystemDataTransferRequest.DATA_TYPE_PERMISSION_SYNC;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
+import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
+
 import static java.util.Objects.requireNonNull;
 
 import android.app.Activity;
-import android.companion.SystemDataTransferRequest;
+import android.companion.datatransfer.PermissionSyncRequest;
+import android.companion.datatransfer.SystemDataTransferRequest;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.ResultReceiver;
-import android.text.Html;
 import android.util.Log;
-import android.view.View;
 import android.widget.Button;
-import android.widget.ListView;
 import android.widget.TextView;
 
 /**
@@ -39,14 +41,18 @@
 
     private static final String LOG_TAG = CompanionDeviceDataTransferActivity.class.getSimpleName();
 
-    // UI -> SystemDataTransferProcessor
-    private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0;
-    private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED = 1;
-    private static final String EXTRA_SYSTEM_DATA_TRANSFER_REQUEST = "system_data_transfer_request";
+    // Intent data keys from SystemDataTransferProcessor
+    private static final String EXTRA_PERMISSION_SYNC_REQUEST = "permission_sync_request";
+    private static final String EXTRA_COMPANION_DEVICE_NAME = "companion_device_name";
     private static final String EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER =
             "system_data_transfer_result_receiver";
 
+    // Intent data keys to SystemDataTransferProcessor
+    private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0;
+    private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED = 1;
+
     private SystemDataTransferRequest mRequest;
+    private CharSequence mCompanionDeviceName;
     private ResultReceiver mCdmServiceReceiver;
 
     @Override
@@ -61,23 +67,27 @@
 
         TextView titleView = findViewById(R.id.title);
         TextView summaryView = findViewById(R.id.summary);
-        ListView listView = findViewById(R.id.device_list);
-        listView.setVisibility(View.GONE);
         Button allowButton = findViewById(R.id.btn_positive);
         Button disallowButton = findViewById(R.id.btn_negative);
 
         final Intent intent = getIntent();
-        mRequest = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_REQUEST);
-        mCdmServiceReceiver = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER);
+        mRequest = intent.getParcelableExtra(EXTRA_PERMISSION_SYNC_REQUEST,
+                PermissionSyncRequest.class);
+        mCompanionDeviceName = intent.getCharSequenceExtra(EXTRA_COMPANION_DEVICE_NAME);
+        mCdmServiceReceiver = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER,
+                ResultReceiver.class);
 
         requireNonNull(mRequest);
         requireNonNull(mCdmServiceReceiver);
 
-        if (mRequest.isPermissionSyncAllPackages()
-                || !mRequest.getPermissionSyncPackages().isEmpty()) {
-            titleView.setText(Html.fromHtml(getString(
-                    R.string.permission_sync_confirmation_title), 0));
-            summaryView.setText(getString(R.string.permission_sync_summary));
+        final String primaryDeviceName = Build.MODEL;
+
+        if (mRequest.getDataType() == DATA_TYPE_PERMISSION_SYNC) {
+            titleView.setText(getHtmlFromResources(this,
+                    R.string.permission_sync_confirmation_title, mCompanionDeviceName,
+                    primaryDeviceName));
+            summaryView.setText(getHtmlFromResources(this, R.string.permission_sync_summary,
+                    mCompanionDeviceName));
             allowButton.setOnClickListener(v -> allow());
             disallowButton.setOnClickListener(v -> disallow());
         }
@@ -101,7 +111,9 @@
 
     private void sendDataToReceiver(int cdmResultCode) {
         Bundle data = new Bundle();
-        data.putParcelable(EXTRA_SYSTEM_DATA_TRANSFER_REQUEST, mRequest);
+        if (mRequest instanceof PermissionSyncRequest) {
+            data.putParcelable(EXTRA_PERMISSION_SYNC_REQUEST, (PermissionSyncRequest) mRequest);
+        }
         mCdmServiceReceiver.send(cdmResultCode, data);
     }
 
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index df2685d..8ef712a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1438,6 +1438,44 @@
     <string name="guest_remove_guest_confirm_button">Remove</string>
     <!-- Status message indicating the device is in the process of resetting the guest user. [CHAR_LIMIT=NONE] -->
     <string name="guest_resetting">Resetting guest\u2026</string>
+    <!-- Dialog title on action reset and restart guest [CHAR LIMIT=60] -->
+    <string name="guest_reset_and_restart_dialog_title">Reset guest session?</string>
+    <!-- Dialog message on action reset and restart guest [CHAR LIMIT=160] -->
+    <string name="guest_reset_and_restart_dialog_message">This will start a new guest
+        session and delete all apps and data from the current session</string>
+    <!-- Dialog title on action exit guest (ephemeral guest) [CHAR LIMIT=32] -->
+    <string name="guest_exit_dialog_title">Exit guest mode?</string>
+    <!-- Dialog message on action exit guest (ephemeral guest) [CHAR LIMIT=80] -->
+    <string name="guest_exit_dialog_message">This will delete
+        apps and data from the current guest session</string>
+    <!-- Dialog button on action exit guest (ephemeral guest) [CHAR LIMIT=80] -->
+    <string name="guest_exit_dialog_button">Exit</string>
+    <!-- Dialog title on action exit guest (non-ephemeral guest) [CHAR LIMIT=32] -->
+    <string name="guest_exit_dialog_title_non_ephemeral">Save guest activity?</string>
+    <!-- Dialog message on action exit guest (non-ephemeral guest) [CHAR LIMIT=80] -->
+    <string name="guest_exit_dialog_message_non_ephemeral">You can save activity from
+        the current session or delete all apps and data</string>
+    <!-- Button on guest exit, clear data (non-ephemeral guest) [CHAR LIMIT=80] -->
+    <string name="guest_exit_clear_data_button">Delete</string>
+    <!-- Button on guest exit, save data (non-ephemeral guest) [CHAR LIMIT=80] -->
+    <string name="guest_exit_save_data_button">Save</string>
+    <!-- Label for button in confirmation dialog when exiting guest user [CHAR LIMIT=35] -->
+    <string name="guest_exit_button">Exit guest mode</string>
+    <!-- Label for button in confirmation dialog when resetting guest user [CHAR LIMIT=35] -->
+    <string name="guest_reset_button">Reset guest session</string>
+    <!-- Label for guest icon in quick settings user switcher [CHAR LIMIT=35] -->
+    <string name="guest_exit_quick_settings_button">Exit guest</string>
+    <!-- Message of the notification when guest mode is entered
+         and it's a ephemeral guest [CHAR LIMIT=60] -->
+    <string name="guest_notification_ephemeral">All activity will be deleted on exit</string>
+    <!-- Message of the notification when guest mode is entered
+         and it's not a ephemeral guest and it's a first time guest login [CHAR LIMIT=60] -->
+    <string name="guest_notification_non_ephemeral">You can save or delete your activity on exit</string>
+    <!-- Message of the notification when guest mode is entered
+         and it's not a ephemeral guest and it's not a first time guest login [CHAR LIMIT=NONE] -->
+    <string name="guest_notification_non_ephemeral_non_first_login">Reset to delete session
+        activity now, or you can save or delete activity on exit</string>
+
     <!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] -->
     <string name="user_image_take_photo">Take a photo</string>
     <!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
index f28572f..69484ed 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
@@ -37,9 +37,9 @@
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.settingslib.R;
+import com.android.settingslib.RestrictedLockUtils;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
@@ -55,7 +55,6 @@
 import java.util.stream.Stream;
 
 @RunWith(RobolectricTestRunner.class)
-@Ignore
 public class EditUserInfoControllerTest {
     private static final int MAX_USER_NAME_LENGTH = 100;
 
@@ -87,6 +86,11 @@
         }
 
         @Override
+        RestrictedLockUtils.EnforcedAdmin getChangePhotoAdminRestriction(Context context) {
+            return null;
+        }
+
+        @Override
         boolean isChangePhotoRestrictedByBase(Context context) {
             return mPhotoRestrictedByBase;
         }
@@ -98,7 +102,7 @@
         mActivity = spy(ActivityController.of(new FragmentActivity()).get());
         mActivity.setTheme(R.style.Theme_AppCompat_DayNight);
         mController = new TestEditUserInfoController();
-        mPhotoRestrictedByBase = true;
+        mPhotoRestrictedByBase = false;
     }
 
     @Test
@@ -262,7 +266,7 @@
 
     @Test
     public void createDialog_canNotChangePhoto_nullPhotoController() {
-        mPhotoRestrictedByBase = false;
+        mPhotoRestrictedByBase = true;
 
         mController.createDialog(mActivity, mActivityStarter, mCurrentIcon,
                 "test", "title", null, null);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 716ee84..a6bfc408b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -43,7 +43,6 @@
         Settings.System.FONT_SCALE,
         Settings.System.DIM_SCREEN,
         Settings.System.SCREEN_OFF_TIMEOUT,
-        Settings.System.SCREEN_OFF_TIMEOUT_DOCKED,
         Settings.System.SCREEN_BRIGHTNESS_MODE,
         Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
         Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index bbfab0b..84b018b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -315,6 +315,9 @@
         VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_WIDTH, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.Wearable.WET_MODE_ON, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.COOLDOWN_MODE_ON, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.TOUCH_AND_HOLD_WATCH_FACE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
     }
 }
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index d430296..06712cc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -111,7 +111,6 @@
                 });
         VALIDATORS.put(System.DISPLAY_COLOR_MODE_VENDOR_HINT, ANY_STRING_VALIDATOR);
         VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
-        VALIDATORS.put(System.SCREEN_OFF_TIMEOUT_DOCKED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255));
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 077337c..700c04c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -5220,7 +5220,7 @@
                                     .getResources()
                                     .getBoolean(R.bool.def_wearable_hotwordDetectionEnabled));
                     initGlobalSettingsDefaultValForWearLocked(
-                            Global.Wearable.SMART_REPLIES_ENABLED, false);
+                            Global.Wearable.SMART_REPLIES_ENABLED, true);
                     Setting locationMode =
                             getSecureSettingsLocked(userId).getSettingLocked(Secure.LOCATION_MODE);
                     initGlobalSettingsDefaultValForWearLocked(
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e358b16..4cdbba7 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -421,6 +421,7 @@
                     Settings.Global.RADIO_NFC,
                     Settings.Global.RADIO_WIFI,
                     Settings.Global.RADIO_WIMAX,
+                    Settings.Global.REMOVE_GUEST_ON_EXIT,
                     Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS,
                     Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT,
                     Settings.Global.RESTRICTED_NETWORKING_MODE,
@@ -663,7 +664,9 @@
                     Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
                     Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER,
                     Settings.Global.Wearable.WET_MODE_ON,
-                    Settings.Global.Wearable.COOLDOWN_MODE_ON);
+                    Settings.Global.Wearable.COOLDOWN_MODE_ON,
+                    Settings.Global.Wearable.TOUCH_AND_HOLD_WATCH_FACE,
+                    Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED);
 
     private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
              newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a28c4cf..1d101ee 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -683,8 +683,8 @@
     <!-- Permission required for CTS test - CtsAppEnumerationTestCases -->
     <uses-permission android:name="android.permission.MAKE_UID_VISIBLE" />
 
-    <!-- Permission required for CTS test - CtsKeystoreTestCases -->
-    <uses-permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION" />
+    <!-- Permission required for CTS test - CtsInputTestCases -->
+    <uses-permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY" />
 
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1ce4c64..68679c79 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -572,6 +572,7 @@
                     break;
                 case INTENT_BUGREPORT_DONE:
                     maybeShowWarningMessageAndCloseNotification(id);
+                    break;
                 case INTENT_BUGREPORT_CANCEL:
                     cancel(id);
                     break;
@@ -843,16 +844,11 @@
                 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
-    @GuardedBy("mLock")
-    private void stopProgressLocked(int id) {
-        stopProgressLocked(id, /* cancelNotification */ true);
-    }
-
     /**
      * Finalizes the progress on a given bugreport and cancel its notification.
      */
     @GuardedBy("mLock")
-    private void stopProgressLocked(int id, boolean cancelNotification) {
+    private void stopProgressLocked(int id) {
         if (mBugreportInfos.indexOfKey(id) < 0) {
             Log.w(TAG, "ID not watched: " + id);
         } else {
@@ -862,12 +858,10 @@
         // Must stop foreground service first, otherwise notif.cancel() will fail below.
         stopForegroundWhenDoneLocked(id);
 
-        if (cancelNotification) {
-            Log.d(TAG, "stopProgress(" + id + "): cancel notification");
-            NotificationManager.from(mContext).cancel(id);
-        } else {
-            Log.d(TAG, "stopProgress(" + id + ")");
-        }
+
+        Log.d(TAG, "stopProgress(" + id + "): cancel notification");
+        NotificationManager.from(mContext).cancel(id);
+
         stopSelfWhenDoneLocked();
     }
 
@@ -1112,30 +1106,7 @@
             return;
         }
 
-        if (mIsWatch) {
-            // Wear wants to send the notification directly and not wait for the user to tap on the
-            // notification.
-            triggerShareBugreportAndLocalNotification(info);
-        } else {
-            triggerLocalNotification(info);
-        }
-    }
-
-    /**
-     * Responsible for starting the bugerport sharing process and posting a notification which
-     * shows that the bugreport has been taken and that the sharing process has kicked-off.
-     */
-    private void triggerShareBugreportAndLocalNotification(final BugreportInfo info) {
-        boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt");
-        if (!isPlainText) {
-            // Already zipped, share it right away.
-            shareBugreport(info.id, info, /* showWarning */ false,
-                /* cancelNotificationWhenStoppingProgress */ false);
-            sendBugreportNotification(info, mTakingScreenshot);
-        } else {
-            // Asynchronously zip the file first, then share it.
-            shareAndPostNotificationForZippedBugreport(info, mTakingScreenshot);
-        }
+        triggerLocalNotification(info);
     }
 
     /**
@@ -1249,16 +1220,14 @@
     }
 
     private void shareBugreport(int id, BugreportInfo sharedInfo) {
-        shareBugreport(id, sharedInfo, !hasUserDecidedNotToGetWarningMessage(),
-            /* cancelNotificationWhenStoppingProgress */ true);
+        shareBugreport(id, sharedInfo, !hasUserDecidedNotToGetWarningMessage());
     }
 
     /**
      * Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
      * intent, but issuing a warning dialog the first time.
      */
-    private void shareBugreport(int id, BugreportInfo sharedInfo, boolean showWarning,
-            boolean cancelNotificationWhenStoppingProgress) {
+    private void shareBugreport(int id, BugreportInfo sharedInfo, boolean showWarning) {
         MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SHARE);
         BugreportInfo info;
         synchronized (mLock) {
@@ -1307,7 +1276,7 @@
         }
         synchronized (mLock) {
             // ... and stop watching this process.
-            stopProgressLocked(id, cancelNotificationWhenStoppingProgress);
+            stopProgressLocked(id);
         }
     }
 
@@ -1362,7 +1331,7 @@
                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
                     .setDeleteIntent(newCancelIntent(mContext, info));
         } else {
-            // Device is a watch.
+            // Device is a watch
             if (hasUserDecidedNotToGetWarningMessage()) {
                 // No action button needed for the notification. User can swipe to dimiss.
                 builder.setActions(new Action[0]);
@@ -1433,24 +1402,6 @@
     }
 
     /**
-     * Zips a bugreport, shares it, and sends for it a bugreport notification.
-     */
-    private void shareAndPostNotificationForZippedBugreport(final BugreportInfo info,
-            final boolean takingScreenshot) {
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... params) {
-                Looper.prepare();
-                zipBugreport(info);
-                shareBugreport(info.id, info, /* showWarning */ false,
-                /* cancelNotificationWhenStoppingProgress */ false);
-                sendBugreportNotification(info, mTakingScreenshot);
-                return null;
-            }
-        }.execute();
-    }
-
-    /**
      * Zips a bugreport file, returning the path to the new file (or to the
      * original in case of failure).
      */
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index f05c1e2..de9e1f4 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -125,6 +125,7 @@
     ],
     manifest: "AndroidManifest.xml",
 
+    javacflags: ["-Adagger.fastInit=enabled"],
     kotlincflags: ["-Xjvm-default=enable"],
 
     plugins: ["dagger2-compiler"],
diff --git a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
index 779ab81..9b43cf6 100644
--- a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
+++ b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
@@ -4,6 +4,6 @@
     android:viewportWidth="24"
     android:viewportHeight="24">
   <path
-      android:fillColor="@color/media_dialog_item_main_content"
+      android:fillColor="@color/media_dialog_inactive_item_main_content"
       android:pathData="M12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,12zM12,20q3.325,0 5.663,-2.337Q20,15.325 20,12t-2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,3.325 2.338,5.663Q8.675,20 12,20z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/media_output_status_check.xml b/packages/SystemUI/res/drawable/media_output_status_check.xml
index 5fbc42b..1b750f8 100644
--- a/packages/SystemUI/res/drawable/media_output_status_check.xml
+++ b/packages/SystemUI/res/drawable/media_output_status_check.xml
@@ -21,6 +21,6 @@
         android:viewportHeight="24"
         android:tint="?attr/colorControlNormal">
     <path
-        android:fillColor="@color/media_dialog_item_main_content"
+        android:fillColor="@color/media_dialog_item_status"
         android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/media_output_status_failed.xml b/packages/SystemUI/res/drawable/media_output_status_failed.xml
index 0599e23..05c6358 100644
--- a/packages/SystemUI/res/drawable/media_output_status_failed.xml
+++ b/packages/SystemUI/res/drawable/media_output_status_failed.xml
@@ -21,6 +21,6 @@
         android:viewportHeight="24"
         android:tint="?attr/colorControlNormal">
     <path
-        android:fillColor="@color/media_dialog_item_main_content"
+        android:fillColor="@color/media_dialog_inactive_item_main_content"
         android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
 </vector>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 1efb479..39c5c45 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -137,7 +137,7 @@
             style="@style/Widget.Dialog.Button.BorderButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/media_output_dialog_button_stop_casting"
+            android:text="@string/keyboard_key_media_stop"
             android:visibility="gone"/>
 
         <Space
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index d39b0d5..eeb37c7 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -33,7 +33,7 @@
             android:layout_height="match_parent"
             android:background="@drawable/media_output_item_background"
             android:layout_gravity="center_vertical|start">
-            <com.android.systemui.media.dialog.MediaOutputSeekbar
+            <SeekBar
                 android:id="@+id/volume_seekbar"
                 android:splitTrack="false"
                 android:visibility="gone"
@@ -83,7 +83,7 @@
                 android:ellipsize="end"
                 android:maxLines="1"
                 android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-                android:textColor="@color/media_dialog_item_main_content"
+                android:textColor="@color/media_dialog_inactive_item_main_content"
                 android:textSize="16sp"/>
             <TextView
                 android:id="@+id/subtitle"
@@ -91,7 +91,7 @@
                 android:layout_height="wrap_content"
                 android:ellipsize="end"
                 android:maxLines="1"
-                android:textColor="@color/media_dialog_item_main_content"
+                android:textColor="@color/media_dialog_inactive_item_main_content"
                 android:textSize="14sp"
                 android:fontFamily="@*android:string/config_bodyFontFamily"
                 android:visibility="gone"/>
@@ -127,7 +127,6 @@
             android:layout_gravity="right|center"
             android:button="@drawable/ic_circle_check_box"
             android:visibility="gone"
-            android:clickable="false"
         />
     </FrameLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 309a95f..8007001 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bind nuwe toestel saam"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Maak die program oop om hierdie sessie uit te saai."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Onbekende program"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Hou op uitsaai"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitsaai werk"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Saai uit"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mense in jou omtrek met versoenbare Bluetooth-toestelle kan na die media luister wat jy uitsaai"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 0cf2d1d..c6762d3 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"አዲስ መሣሪያ ያጣምሩ"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ይህን ክፍለ ጊዜ cast ለማድረግ፣ እባክዎ መተግበሪያውን ይክፈቱ።"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"የማይታወቅ መተግበሪያ"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Cast ማድረግ አቁም"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ማሰራጨት እንዴት እንደሚሠራ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ስርጭት"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ተኳሃኝ የብሉቱዝ መሣሪያዎች ያላቸው በአቅራቢያዎ ያሉ ሰዎች እርስዎ እያሰራጩት ያሉትን ሚዲያ ማዳመጥ ይችላሉ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index cd1f635..932061e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -860,7 +860,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"إقران جهاز جديد"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"لبث هذه الجلسة، يُرجى فتح التطبيق"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"تطبيق غير معروف"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"إيقاف البث"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"كيفية عمل البث"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"البث"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"يمكن للأشخاص القريبين منك الذين لديهم أجهزة متوافقة تتضمّن بلوتوث الاستماع إلى الوسائط التي تبثها."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 12f133c..316a452 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"এই ছেশ্বনটো কাষ্ট কৰিবলৈ, অনুগ্ৰহ কৰি এপ্‌টো খোলক"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"অজ্ঞাত এপ্"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"কাষ্ট বন্ধ কৰক"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"সম্প্ৰচাৰ কৰাটোৱে কেনেকৈ কাম কৰে"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্ৰচাৰ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"সমিল ব্লুটুথ ডিভাইচৰ সৈতে আপোনাৰ নিকটৱৰ্তী স্থানত থকা লোকসকলে আপুনি সম্প্ৰচাৰ কৰা মিডিয়াটো শুনিব পাৰে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 39524c6..44f4117 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Cihaz əlavə edin"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Bu sessiyanı yayımlamaq üçün tətbiqi açın."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Naməlum tətbiq"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Yayımı dayandırın"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayım necə işləyir"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Yayım"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Uyğun Bluetooth cihazları olan yaxınlığınızdakı insanlar yayımladığınız medianı dinləyə bilər"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index e9839b9..69c3c8a 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -842,7 +842,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Da biste prebacivali ovu sesiju, otvorite aplikaciju."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nepoznata aplikacija"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zaustavi prebacivanje"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcioniše emitovanje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emitovanje"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ljudi u blizini sa kompatibilnim Bluetooth uređajima mogu da slušaju medijski sadržaj koji emitujete"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 2c8ab6f..d3ef660 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -848,7 +848,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спалучыць з новай прыладай"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Для трансляцыі гэтага сеанса адкрыйце праграму."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Невядомая праграма"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Спыніць трансляцыю"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як адбываецца трансляцыя"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Трансляцыя"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Людзі паблізу, у якіх ёсць прылады з Bluetooth, змогуць праслухваць мультымедыйнае змесціва, якое вы трансліруеце"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 376ed06..5d0b2bb 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Сдвояване на ново устройство"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"За да предавате тази сесия, моля, отворете приложението."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Неизвестно приложение"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Спиране на предаването"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работи предаването"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Предаване"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Хората в близост със съвместими устройства с Bluetooth могат да слушат мултимедията, която предавате"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 4fc09a2..5e0c64f 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইস পেয়ার করুন"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"এই সেশন কাস্ট করার জন্য, অ্যাপ খুলুন।"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"অজানা অ্যাপ"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"কাস্ট করা বন্ধ করুন"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ব্রডকাস্ট কীভাবে কাজ করে"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"সম্প্রচার করুন"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"আশপাশে লোকজন যাদের মানানসই ব্লুটুথ ডিভাইস আছে, তারা আপনার ব্রডকাস্ট করা মিডিয়া শুনতে পারবেন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index e692d3c..bd2b4d0 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -839,7 +839,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Da emitirate ovu sesiju, otvorite aplikaciju."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nepoznata aplikacija"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zaustavi emitiranje"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcionira emitiranje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emitirajte"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u vašoj blizini s kompatibilnim Bluetooth uređajima mogu slušati medijske sadržaje koje emitirate"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index f8eb7b2..ab7cba8 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincula un dispositiu nou"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Per emetre aquesta sessió, obre l\'aplicació."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicació desconeguda"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Atura l\'emissió"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Com funciona l\'emissió"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emet"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les persones properes amb dispositius Bluetooth compatibles poden escoltar el contingut multimèdia que emets"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index eec539f..77761be 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -848,7 +848,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovat nové zařízení"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pokud chcete odesílat relaci, otevřete aplikaci."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Neznámá aplikace"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zastavit odesílání"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak vysílání funguje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Vysílání"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lidé ve vašem okolí s kompatibilními zařízeními Bluetooth mohou poslouchat média, která vysíláte"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index a1eb92a..3636730 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Par ny enhed"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Åbn appen for at caste denne session."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ukendt app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop med at caste"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Sådan fungerer udsendelser"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Udsendelse"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i nærheden, som har kompatible Bluetooth-enheder, kan lytte til det medie, du udsender"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 96d77ba..05956d9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Neues Gerät koppeln"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Öffne zum Streamen dieser Sitzung die App."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unbekannte App"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Streaming beenden"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Funktionsweise von Nachrichten an alle"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Nachricht an alle"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personen, die in der Nähe sind und kompatible Bluetooth-Geräten haben, können sich die Medien anhören, die du per Nachricht an alle sendest"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 319dca5..b4af947 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Σύζευξη νέας συσκευής"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Για μετάδοση της περιόδου σύνδεσης, ανοίξτε την εφαρμογή."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Άγνωστη εφαρμογή"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Διακοπή μετάδοσης"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Πώς λειτουργεί η μετάδοση"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Μετάδοση"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Οι άνθρωποι με συμβατές συσκευές Bluetooth που βρίσκονται κοντά σας μπορούν να ακούσουν το μέσο που μεταδίδετε."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index b3ab570..d42cc52 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 6939dfc..c5f7445 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index b3ab570..d42cc52 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index b3ab570..d42cc52 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"To cast this session, please open the app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Unknown app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stop casting"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 53cd65b..601305b 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎Pair new device‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎To cast this session, please open the app.‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎Unknown app‎‏‎‎‏‎"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‎‏‎Stop casting‎‏‎‎‏‎"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎How broadcasting works‎‏‎‎‏‎"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎Broadcast‎‏‎‎‏‎"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎People near you with compatible Bluetooth devices can listen to the media you\'re broadcasting‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 8fb5623..416e097 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo nuevo"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para transmitir esta sesión, abre la app"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconocida"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Detener transmisión"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la transmisión"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmisión"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que transmites"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index e2f0b9b..35fbf2a 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Emparejar nuevo dispositivo"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para enviar esta sesión, abre la aplicación."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicación desconocida"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Dejar de enviar contenido"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emisión"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que emites"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 59e18e0..a06fd3a 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uue seadme sidumine"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Selle seansi ülekandmiseks avage rakendus."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Tundmatu rakendus"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Lõpeta ülekanne"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kuidas ülekandmine toimib?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Ülekanne"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Teie läheduses olevad inimesed, kellel on ühilduvad Bluetooth-seadmed, saavad kuulata teie ülekantavat meediat"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 0f2aecc..a880334 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parekatu beste gailu batekin"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Saioa ireki nahi baduzu, ireki aplikazioa."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplikazio ezezaguna"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Utzi igortzeari"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Nola funtzionatzen dute iragarpenek?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Iragarri"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth bidezko gailu bateragarriak dituzten inguruko pertsonek iragartzen ari zaren multimedia-edukia entzun dezakete"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index c4255db..13cccf0 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"مرتبط کردن دستگاه جدید"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"برای ارسال محتوای این جلسه، لطفاً برنامه را باز کنید."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"برنامه ناشناس"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"توقف ارسال محتوا"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"همه‌فرتستی چطور کار می‌کند"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"همه‌فرستی"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‏افرادی که در اطرافتان دستگاه‌های Bluetooth سازگار دارند می‌توانند به رسانه‌ای که همه‌فرستی می‌کنید گوش کنند"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index f5b20a0..823c526 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Muodosta uusi laitepari"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Jos haluat striimata tämän käyttökerran, avaa sovellus."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Tuntematon sovellus"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Lopeta striimaus"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Miten lähetys toimii"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Lähetys"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lähistöllä olevat ihmiset, joilla on yhteensopiva Bluetooth-laite, voivat kuunnella lähettämääsi mediaa"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 61eeb0c..f78773d 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un autre appareil"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pour diffuser cette session, veuillez ouvrir l\'application."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Application inconnue"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Arrêter la diffusion"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement de la diffusion"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Diffusion"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité disposant d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 17f1468..1bdf0f2 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un nouvel appareil"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pour caster cette session, veuillez ouvrir l\'appli."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Appli inconnue"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Arrêter la diffusion"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement des annonces"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Annonce"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité équipées d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index a3655f7..78bbb28 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo novo"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para emitir esta sesión, abre a aplicación."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicación descoñecida"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Deter emisión"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funcionan as difusións?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Difusión"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As persoas que estean preto de ti e que dispoñan de dispositivos Bluetooth compatibles poden escoitar o contido multimedia que difundas"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 6141011..6085d76 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"આ સત્ર કાસ્ટ કરવા માટે, કૃપા કરીને ઍપ ખોલો."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"અજાણી ઍપ"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"કાસ્ટ કરવાનું રોકો"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"બ્રોડકાસ્ટ પ્રક્રિયાની કામ કરવાની રીત"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"બ્રોડકાસ્ટ કરો"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"સુસંગત બ્લૂટૂથ ડિવાઇસ ધરાવતા નજીકના લોકો તમે જે મીડિયા બ્રોડકાસ્ટ કરી રહ્યાં છો તે સાંભળી શકે છે"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1971298..49bafd4 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नया डिवाइस जोड़ें"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्टिंग करना रोकें"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्ट करने की सुविधा कैसे काम करती है"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करें"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"आपके आस-पास मौजूद लोग, ब्रॉडकास्ट किए जा रहे मीडिया को सुन सकते हैं. हालांकि, इसके लिए उनके पास ऐसे ब्लूटूथ डिवाइस होने चाहिए जिन पर मीडिया चलाया जा सके"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index b9bf553..54b3912 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -839,7 +839,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Da biste emitirali ovu sesiju, otvorite aplikaciju."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nepoznata aplikacija"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zaustavi emitiranje"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako emitiranje funkcionira"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emitiranje"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u blizini s kompatibilnim Bluetooth uređajima mogu slušati medije koje emitirate"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 4a8bca3..3d2d01b 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Új eszköz párosítása"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"A munkamenet átküldéséhez nyissa meg az alkalmazást."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ismeretlen alkalmazás"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Átküldés leállítása"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"A közvetítés működése"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Közvetítés"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"A közelben tartózkodó, kompatibilis Bluetooth-eszközzel rendelkező személyek meghallgathatják az Ön közvetített médiatartalmait"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 31dafac..aa03db0 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Նոր սարքի զուգակցում"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Այս աշխատաշրջանը հեռարձակելու համար բացեք հավելվածը"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Անհայտ հավելված"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Կանգնեցնել հեռարձակումը"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ինչպես է աշխատում հեռարձակումը"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Հեռարձակում"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ձեր մոտակայքում գտնվող՝ համատեղելի Bluetooth սարքերով մարդիկ կարող են լսել մեդիա ֆայլերը, որոնք դուք հեռարձակում եք։"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 9bdca97..396690f 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sambungkan perangkat baru"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Buka aplikasi untuk mentransmisikan sesi ini."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplikasi tidak dikenal"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Hentikan transmisi"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara kerja siaran"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Siaran"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang di dekat Anda dengan perangkat Bluetooth yang kompatibel dapat mendengarkan media yang sedang Anda siarkan"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 2311a56..251e4e2 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Para nýtt tæki"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Opnaðu forritið til að senda þessa lotu út."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Óþekkt forrit"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stöðva útsendingu"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Svona virkar útsending"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Útsending"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Fólk nálægt þér með samhæf Bluetooth-tæki getur hlustað á efnið sem þú sendir út"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 029505d..84b1ffd 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Accoppia nuovo dispositivo"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Per trasmettere questa sessione devi aprire l\'app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App sconosciuta"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Interrompi trasmissione"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Come funziona la trasmissione"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Annuncio"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Le persone vicine a te che hanno dispositivi Bluetooth compatibili possono ascoltare i contenuti multimediali che stai trasmettendo"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 29e9c30..bc2918d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -848,7 +848,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"התאמה של מכשיר חדש"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"‏כדי להעביר (cast) את הסשן הזה, צריך לפתוח את האפליקציה."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"אפליקציה לא ידועה"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"‏עצירת ההעברה (casting)"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"הסבר על שידורים"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"שידור"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‏אנשים בקרבת מקום עם מכשירי Bluetooth תואמים יכולים להאזין למדיה שמשודרת על ידך"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 519b824..5594fdd 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"新しいデバイスとのペア設定"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"このセッションをキャストするには、アプリを開いてください。"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"不明なアプリ"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"キャストを停止"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ブロードキャストの仕組み"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ブロードキャスト"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth 対応デバイスを持っている付近のユーザーは、あなたがブロードキャストしているメディアを聴けます"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 1eba25d..d7fb9fe 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ახალი მოწყობილობის დაწყვილება"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ამ სესიის ტრანსლირებისთვის გახსენით აპი."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"უცნობი აპი"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ტრანსლირების შეწყვეტა"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ტრანსლირების მუშაობის პრინციპი"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ტრანსლაცია"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"თქვენთან ახლოს მყოფ ხალხს თავსებადი Bluetooth მოწყობილობით შეუძლიათ თქვენ მიერ ტრანსლირებული მედიის მოსმენა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index be57f46..0534486 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңа құрылғымен жұптау"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Бұл сеансты трансляциялау үшін қолданбаны ашыңыз."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Белгісіз қолданба"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Трансляцияны тоқтату"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Тарату қалай жүзеге асады"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Тарату"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Үйлесімді Bluetooth құрылғылары бар маңайдағы адамдар сіз таратып жатқан медиамазмұнды тыңдай алады."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 59208f5..fc400e3 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ផ្គូផ្គង​ឧបករណ៍ថ្មី"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ដើម្បីភ្ជាប់វគ្គនេះ សូមបើកកម្មវិធី។"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"កម្មវិធី​ដែលមិន​ស្គាល់"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"បញ្ឈប់ការភ្ជាប់"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"របៀបដែលការផ្សាយដំណើរការ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ការ​ផ្សាយ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"មនុស្សនៅជិត​អ្នកដែលមាន​ឧបករណ៍ប៊្លូធូស​ត្រូវគ្នា​អាចស្តាប់​មេឌៀ​ដែលអ្នកកំពុងផ្សាយបាន"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 0037799..865808a 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ಈ ಸೆಶನ್ ಕಾಸ್ಟ್ ಮಾಡಲು, ಆ್ಯಪ್ ಅನ್ನು ತೆರೆಯಿರಿ."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ಅಪರಿಚಿತ ಆ್ಯಪ್"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ಬಿತ್ತರಿಸುವುದನ್ನು ನಿಲ್ಲಿಸಿ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ಪ್ರಸಾರವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ಪ್ರಸಾರ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ಹೊಂದಾಣಿಕೆಯಾಗುವ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ಹೊಂದಿರುವ ಸಮೀಪದಲ್ಲಿರುವ ಜನರು ನೀವು ಪ್ರಸಾರ ಮಾಡುತ್ತಿರುವ ಮಾಧ್ಯಮವನ್ನು ಆಲಿಸಬಹುದು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 24bff17..d52942a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"새 기기와 페어링"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"세션을 전송하려면 앱을 열어 주세요"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"알 수 없는 앱"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"전송 중지"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"브로드캐스팅 작동 원리"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"브로드캐스트"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"호환되는 블루투스 기기를 가진 근처의 사용자가 내가 브로드캐스트 중인 미디어를 수신 대기할 수 있습니다."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 552822c..a1b1c00 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңы түзмөк кошуу"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Бул сеансты тышкы экранга чыгаруу үчүн колдонмону ачыңыз."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Белгисиз колдонмо"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Тышкы экранга чыгарууну токтотуу"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Кабарлоо кантип иштейт"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Кабарлоо"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Шайкеш Bluetooth түзмөктөрү болгон жакын жердеги кишилер кабарлап жаткан медиаңызды уга алышат"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 8108efd..26840cb 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ເພື່ອສົ່ງສັນຍານເຊດຊັນນີ້, ກະລຸນາເປີດແອັບ."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ແອັບທີ່ບໍ່ຮູ້ຈັກ"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ຢຸດການສົ່ງສັນຍານ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ການອອກອາກາດເຮັດວຽກແນວໃດ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ອອກອາກາດ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ຄົນທີ່ຢູ່ໃກ້ທ່ານທີ່ມີອຸປະກອນ Bluetooth ທີ່ເຂົ້າກັນໄດ້ຈະສາມາດຟັງມີເດຍທີ່ທ່ານກຳລັງອອກອາກາດຢູ່ໄດ້"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 619d256..c9bd89b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -845,7 +845,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Naujo įrenginio susiejimas"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Jei norite perduoti šį seansą, atidarykite programą."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nežinoma programa"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Sustabdyti perdavimą"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kaip veikia transliacija"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transliacija"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Netoliese esantys žmonės, turintys suderinamus „Bluetooth“ įrenginius, gali klausyti jūsų transliuojamos medijos"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 62f06f7..e26e2ba 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -842,7 +842,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Savienošana pārī ar jaunu ierīci"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Lai apraidītu šo sesiju, lūdzu, atveriet lietotni."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nezināma lietotne"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Apturēt apraidi"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kā darbojas apraide"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Apraide"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Tuvumā esošās personas ar saderīgām Bluetooth ierīcēm var klausīties jūsu apraidīto multivides saturu."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 195af88..34c742b 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спарете нов уред"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"За да ја емитувате сесијава, отворете ја апликацијата."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Непозната апликација"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Сопри со емитување"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционира емитувањето"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Емитување"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Луѓето во ваша близина со компатибилни уреди со Bluetooth може да ги слушаат аудиозаписите што ги емитувате"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 4b4b098..61b2101 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"പുതിയ ഉപകരണവുമായി ജോടിയാക്കുക"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ഈ സെഷൻ കാസ്റ്റ് ചെയ്യാൻ, ആപ്പ് തുറക്കുക."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"അജ്ഞാതമായ ആപ്പ്"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"കാസ്റ്റ് ചെയ്യുന്നത് നിർത്തുക"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ബ്രോഡ്‌കാസ്‌റ്റ് എങ്ങനെയാണ് പ്രവർത്തിക്കുന്നത്"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ബ്രോഡ്‌കാസ്റ്റ്"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"അനുയോജ്യമായ Bluetooth ഉപകരണങ്ങളോടെ സമീപമുള്ള ആളുകൾക്ക് നിങ്ങൾ ബ്രോഡ്‌കാസ്‌റ്റ് ചെയ്യുന്ന മീഡിയ കേൾക്കാനാകും"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 5f2cb81..2627d62 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Шинэ төхөөрөмж хослуулах"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Энэ үйл явдлыг дамжуулахын тулд аппыг нээнэ үү."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Үл мэдэгдэх апп"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Дамжуулахыг зогсоох"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Нэвтрүүлэлт хэрхэн ажилладаг вэ?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Нэвтрүүлэлт"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Тохиромжтой Bluetooth төхөөрөмжүүдтэй таны ойролцоох хүмүүс таны нэвтрүүлж буй медиаг сонсох боломжтой"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 96ba728..ffe91ba 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नवीन डिव्हाइससोबत पेअर करा"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"हे सेशन कास्ट करण्यासाठी, कृपया ॲप उघडा."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अज्ञात अ‍ॅप"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करणे थांबवा"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्टिंग कसे काम करते"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ब्रॉडकास्ट करा"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"कंपॅटिबिल ब्लूटूथ डिव्‍हाइस असलेले तुमच्या जवळपासचे लोक हे तुम्ही ब्रॉडकास्ट करत असलेला मीडिया ऐकू शकतात"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 0240e9c..d2596dc 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Gandingkan peranti baharu"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Untuk menghantar sesi ini, sila buka apl."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Apl yang tidak diketahui"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Berhenti menghantar"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara siaran berfungsi"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Siarkan"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang berdekatan anda dengan peranti Bluetooth yang serasi boleh mendengar media yang sedang anda siarkan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 2ee7850..e795c45 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"စက်အသစ် တွဲချိတ်ရန်"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"အက်ပ်ဖွင့်ပြီး ဤစက်ရှင်ကို ကာစ်လုပ်နိုင်သည်။"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"အမည်မသိ အက်ပ်"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ကာစ် ရပ်ရန်"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ထုတ်လွှင့်မှုဆောင်ရွက်ပုံ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ထုတ်လွှင့်ခြင်း"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"အနီးရှိတွဲသုံးနိုင်သော ဘလူးတုသ်သုံးစက် အသုံးပြုသူများက သင်ထုတ်လွှင့်နေသော မီဒီယာကို နားဆင်နိုင်သည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 7b81157..7a3d90f 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Koble til en ny enhet"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"For å caste denne økten, åpne appen."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ukjent app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Stopp castingen"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Slik fungerer kringkasting"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Kringkasting"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Folk i nærheten med kompatible Bluetooth-enheter kan lytte til mediene du kringkaster"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index f0d2601..8bb48ac 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"यो सत्र कास्ट गर्न चाहनुहुन्छ भने कृपया एप खोल्नुहोस्।"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अज्ञात एप"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट गर्न छाड्नुहोस्"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"प्रसारण गर्ने सुविधाले कसरी काम गर्छ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"प्रसारण"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"कम्प्याटिबल ब्लुटुथ डिभाइस भएका नजिकैका मान्छेहरू तपाईंले प्रसारण गरिरहनुभएको मिडिया सुन्न सक्छन्"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 3a638b1..4b96d5d 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -67,12 +67,10 @@
 
     <!-- media output dialog-->
     <color name="media_dialog_background">@color/material_dynamic_neutral10</color>
-    <color name="media_dialog_item_main_content">@color/material_dynamic_primary90</color>
-    <color name="media_dialog_item_background">@color/material_dynamic_neutral_variant20</color>
-    <color name="media_dialog_connected_item_background">@color/material_dynamic_secondary20</color>
-    <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color>
-    <color name="media_dialog_button_background">@color/material_dynamic_primary70</color>
-    <color name="media_dialog_solid_button_text">@color/material_dynamic_secondary20</color>
+    <color name="media_dialog_active_item_main_content">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_item_status">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
 
     <!-- Biometric dialog colors -->
     <color name="biometric_dialog_gray">#ffcccccc</color>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index acb194c..d25f7d3 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Nieuw apparaat koppelen"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Als je deze sessie wilt casten, open je de app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Onbekende app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Casten stoppen"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitzenden werkt"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Uitzending"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mensen bij jou in de buurt met geschikte bluetooth-apparaten kunnen luisteren naar de media die je uitzendt"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index e5fb88c..4395fb5 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ନୂଆ ଡିଭାଇସକୁ ପେୟାର୍ କରନ୍ତୁ"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ଏହି ସେସନକୁ କାଷ୍ଟ କରିବା ପାଇଁ, ଦୟାକରି ଆପ ଖୋଲନ୍ତୁ।"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ଅଜଣା ଆପ"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"କାଷ୍ଟ କରିବା ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ବ୍ରଡକାଷ୍ଟିଂ କିପରି କାମ କରେ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ବ୍ରଡକାଷ୍ଟ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ଆପଣଙ୍କ ଆଖପାଖର କମ୍ପାଟିବଲ ବ୍ଲୁଟୁଥ ଡିଭାଇସ ଥିବା ଲୋକମାନେ ଆପଣ ବ୍ରଡକାଷ୍ଟ କରୁଥିବା ମିଡିଆ ଶୁଣିପାରିବେ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 132de78..99c97d86 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ਇਸ ਸੈਸ਼ਨ ਨੂੰ ਕਾਸਟ ਕਰਨ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਖੋਲ੍ਹੋ।"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"ਅਗਿਆਤ ਐਪ"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰੋ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ਪ੍ਰਸਾਰਨ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ਪ੍ਰਸਾਰਨ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ਅਨੁਰੂਪ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਨਾਲ ਨਜ਼ਦੀਕੀ ਲੋਕ ਤੁਹਾਡੇ ਵੱਲੋਂ ਪ੍ਰਸਾਰਨ ਕੀਤੇ ਜਾ ਰਹੇ ਮੀਡੀਆ ਨੂੰ ਸੁਣ ਸਕਦੇ ਹਨ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 2d560fa..c1d86fc 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -848,7 +848,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sparuj nowe urządzenie"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Aby przesłać tę sesję, otwórz aplikację."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Nieznana aplikacja"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zatrzymaj przesyłanie"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak działa transmitowanie"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmisja"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osoby w pobliżu ze zgodnymi urządzeniami Bluetooth mogą słuchać transmitowanych przez Ciebie multimediów"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 464414d6..696628e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Abra o app para transmitir esta sessão."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconhecido"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index d1124b6..0442277 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sincronize o novo dispositivo"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para transmitir esta sessão, abra a app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconhecida"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmissão"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas de si com dispositivos Bluetooth compatíveis podem ouvir o conteúdo multimédia que está a transmitir"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 464414d6..696628e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Abra o app para transmitir esta sessão."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"App desconhecido"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Parar transmissão"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index d943d11..406fe66 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -842,7 +842,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Asociați un nou dispozitiv"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pentru a proiecta această sesiune, deschideți aplicația."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicație necunoscută"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Nu mai proiectați"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cum funcționează transmisia"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmiteți"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Persoanele din apropiere cu dispozitive Bluetooth compatibile pot asculta conținutul pe care îl transmiteți"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index bf35e93..9a7367a 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -848,7 +848,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Подключить новое устройство"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Чтобы начать трансляцию сеанса, откройте приложение"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Неизвестное приложение"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Остановить трансляцию"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работают трансляции"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Трансляция"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Находящиеся рядом с вами люди с совместимыми устройствами Bluetooth могут слушать медиафайлы, которые вы транслируете."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 1d01553..b83fcae 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"නව උපාංගය යුගල කරන්න"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"මෙම සැසිය විකාශය කිරීමට, කරුණාකර යෙදුම විවෘත කරන්න."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"නොදන්නා යෙදුම"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"විකාශය නවතන්න"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"විකාශනය ක්‍රියා කරන ආකාරය"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"විකාශනය"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ගැළපෙන බ්ලූටූත් උපාංග සහිත ඔබ අවට සිටින පුද්ගලයින්ට ඔබ විකාශනය කරන මාධ්‍යයට සවන් දිය හැකිය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index f45fc9d..9abb7f6 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -848,7 +848,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovať nové zariadenie"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Ak chcete túto reláciu prenášať, otvorte aplikáciu."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Neznáma aplikácia"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Zastaviť prenos"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ako vysielanie funguje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Vysielanie"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ľudia v okolí s kompatibilnými zariadeniami s rozhraním Bluetooth si môžu vypočuť médiá, ktoré vysielate"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 134dba5..1e645fb 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -848,7 +848,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Seznanitev nove naprave"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Če želite predvajati to sejo, odprite aplikacijo."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Neznana aplikacija"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ustavi predvajanje"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako deluje oddajanje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Oddajanje"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osebe v bližini z združljivo napravo Bluetooth lahko poslušajo predstavnost, ki jo oddajate."</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 3e7cbb0..f150487 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Çifto pajisjen e re"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Hap aplikacionin për të transmetuar këtë seancë."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplikacion i panjohur"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ndalo transmetimin"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Si funksionon transmetimi"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmetimi"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personat në afërsi me ty me pajisje të përputhshme me Bluetooth mund të dëgjojnë median që ti po transmeton"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 0991763..ffb5ab1 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -842,7 +842,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Упари нови уређај"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Да бисте пребацивали ову сесију, отворите апликацију."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Непозната апликација"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Заустави пребацивање"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционише емитовање"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Емитовање"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Људи у близини са компатибилним Bluetooth уређајима могу да слушају медијски садржај који емитујете"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c6c9d11..3a016e6 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parkoppla en ny enhet"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Öppna appen om du vill casta den här sessionen."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Okänd app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Sluta casta"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Så fungerar utsändning"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Utsändning"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i närheten med kompatibla Bluetooth-enheter kan lyssna på medieinnehåll som du sänder ut"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 410a661..64ab1fa 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Oanisha kifaa kipya"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Ili utume kipindi hiki, tafadhali fungua programu."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Programu isiyojulikana"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Acha kutuma"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jinsi utangazaji unavyofanya kazi"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Tangaza"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Watu walio karibu nawe wenye vifaa oanifu vya Bluetooth wanaweza kusikiliza maudhui unayoyatangaza"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index a2f85c7..15f5bbc 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"புதிய சாதனத்தை இணைத்தல்"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"இந்த அமர்வை அலைபரப்ப ஆப்ஸைத் திறங்கள்."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"அறியப்படாத ஆப்ஸ்"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"அலைபரப்புவதை நிறுத்து"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"பிராட்காஸ்ட் எவ்வாறு செயல்படுகிறது?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"பிராட்காஸ்ட்"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"நீங்கள் பிராட்காஸ்ட் செய்யும் மீடியாவை அருகிலுள்ளவர்கள் இணக்கமான புளூடூத் சாதனங்கள் மூலம் கேட்கலாம்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 12542af..8aea061 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"ఈ సెషన్‌ను ప్రసారం చేయడానికి, దయచేసి యాప్‌ను తెరవండి."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"తెలియని యాప్"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"ప్రసారాన్ని ఆపివేయండి"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ప్రసారం కావడం అనేది ఎలా పని చేస్తుంది"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ప్రసారం"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"మీకు సమీపంలో ఉన్న వ్యక్తులు అనుకూలత ఉన్న బ్లూటూత్ పరికరాలతో మీరు ప్రసారం చేస్తున్న మీడియాను వినగలరు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 2ff5315..78e138d 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"จับคู่อุปกรณ์ใหม่"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"โปรดเปิดแอปหากต้องการแคสต์เซสชันนี้"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"แอปที่ไม่รู้จัก"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"หยุดแคสต์"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"วิธีการทำงานของการออกอากาศ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ประกาศ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ผู้ที่อยู่ใกล้คุณและมีอุปกรณ์บลูทูธที่รองรับสามารถรับฟังสื่อที่คุณกำลังออกอากาศได้"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index b0c117a..9721b75 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Magpares ng bagong device"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Para ma-cast ang session na ito, buksan ang app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Hindi kilalang app"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Ihinto ang pag-cast"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Paano gumagana ang pag-broadcast"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Makakapakinig ang mga taong malapit sa iyo na may mga compatible na Bluetooth device sa media na bino-broadcast mo"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 00ce33d..dfbdcb9 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihaz eşle"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Bu oturumu yayınlamak için lütfen uygulamayı açın."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Bilinmeyen uygulama"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Yayını durdur"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayınlamanın işleyiş şekli"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Anons"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Yakınınızda ve uyumlu Bluetooth cihazları olan kişiler yayınladığınız medya içeriğini dinleyebilir"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c5cf9fa..5fb6759 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -848,7 +848,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Підключити новий пристрій"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Щоб транслювати цей сеанс, відкрийте додаток."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Невідомий додаток"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Припинити трансляцію"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як працює трансляція"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Трансляція"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Люди поблизу, які мають сумісні пристрої з Bluetooth, можуть слухати медіаконтент, який ви транслюєте."</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 55778f8..bd9bb2f 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"نئے آلہ کا جوڑا بنائیں"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"اس سیشن کو کاسٹ کرنے کیلئے، براہ کرم ایپ کھولیں۔"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"نامعلوم ایپ"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"کاسٹ کرنا بند کریں"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"براڈکاسٹنگ کیسے کام کرتا ہے"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"براڈکاسٹ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"موافق بلوٹوتھ آلات کے ساتھ آپ کے قریبی لوگ آپ کے نشر کردہ میڈیا کو سن سکتے ہیں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 362208f..9b6295d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yangi qurilmani ulash"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Bu seansni translatsiya qilish uchun ilovani oching."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Notanish ilova"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Toʻxtatish"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Translatsiya qanday ishlaydi"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Translatsiya"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Atrofingizdagi mos Bluetooth qurilmasiga ega foydalanuvchilar siz translatsiya qilayotgan mediani tinglay olishadi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index a2f9dd2..508fdb1 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Ghép nối thiết bị mới"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Vui lòng mở ứng dụng để truyền phiên này."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Ứng dụng không xác định"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Dừng truyền"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cách tính năng truyền hoạt động"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Truyền"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Những người ở gần có thiết bị Bluetooth tương thích có thể nghe nội dung nghe nhìn bạn đang truyền"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d8f4457..fdee02f 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"与新设备配对"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"如需投射此会话,请打开相关应用。"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"未知应用"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投射"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"广播的运作方式"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"广播"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近使用兼容蓝牙设备的用户可以收听您广播的媒体内容"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 73e4c0e..d20a9a0 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -833,7 +833,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"如要投放此工作階段,請開啟應用程式。"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"不明應用程式"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播運作方式"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"附近有兼容藍牙裝置的人可收聽您正在廣播的媒體內容"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index b1cf81e..6e64ce3 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"如要投放這個工作階段,請開啟應用程式。"</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"不明的應用程式"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"停止投放"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播功能的運作方式"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"廣播"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"如果附近的人有相容的藍牙裝置,就可以聽到你正在廣播的媒體內容"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 4f9262f..f4b62e7 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -836,7 +836,6 @@
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bhangqa idivayisi entsha"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Ukuze usakaze le seshini, sicela uvule i-app."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"I-app engaziwa"</string>
-    <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Misa ukusakaza"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Indlela ukusakaza okusebenza ngayo"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Sakaza"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Abantu abaseduze nawe abanamadivayisi e-Bluetooth ahambisanayo bangalalela imidiya oyisakazayo"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 24118d2..a06201c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -179,12 +179,10 @@
 
     <!-- media output dialog-->
     <color name="media_dialog_background" android:lstar="98">@color/material_dynamic_neutral90</color>
-    <color name="media_dialog_item_main_content">@color/material_dynamic_primary20</color>
+    <color name="media_dialog_active_item_main_content">@color/material_dynamic_primary10</color>
+    <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_primary40</color>
+    <color name="media_dialog_item_status">@color/material_dynamic_primary10</color>
     <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
-    <color name="media_dialog_connected_item_background">@color/material_dynamic_primary90</color>
-    <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color>
-    <color name="media_dialog_button_background">@color/material_dynamic_primary40</color>
-    <color name="media_dialog_solid_button_text">@color/material_dynamic_neutral95</color>
 
     <!-- controls -->
     <color name="control_primary_text">#E6FFFFFF</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b248efe..c2d7f819 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -888,6 +888,23 @@
     <!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] -->
     <string name="guest_wipe_session_dontwipe">Yes, continue</string>
 
+    <!-- App name of the notification when guest mode is entered [CHAR LIMIT=35] -->
+    <string name="guest_notification_app_name">Guest mode</string>
+    <!-- Title of the notification when guest mode is entered [CHAR LIMIT=35] -->
+    <string name="guest_notification_session_active">You are in guest mode</string>
+
+    <!-- Title for add user confirmation dialog [CHAR LIMIT=30] -->
+    <string name="user_add_user_title" msgid="2108112641783146007">Add new user?</string>
+
+    <!-- Message for add user confirmation dialog - short version. [CHAR LIMIT=none] -->
+    <string name="user_add_user_message_short" msgid="1511354412249044381">When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. </string>
+
+    <!-- Additional message for add user confirmation dialog that is appended when current user is
+         guest and guest is ephemeral. This is to warn users that current guest session
+         would get removed after a new user is added and switched to [CHAR LIMIT=none] -->
+    <string name="user_add_user_message_guest_remove">\n\nAdding a new user will exit guest mode
+        and delete all apps and data from the current guest session.</string>
+
     <!-- Title for the dialog that lets users know that the maximum allowed number of users on the device has been reached. [CHAR LIMIT=35]-->
     <string name="user_limit_reached_title">User limit reached</string>
 
@@ -2262,8 +2279,6 @@
     <string name="media_output_dialog_launch_app_text">To cast this session, please open the app.</string>
     <!-- App name when can't get app name [CHAR LIMIT=60] -->
     <string name="media_output_dialog_unknown_launch_app_name">Unknown app</string>
-    <!-- Button text for stopping casting [CHAR LIMIT=60] -->
-    <string name="media_output_dialog_button_stop_casting">Stop casting</string>
 
     <!-- Media Output Broadcast Dialog -->
     <!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f97bbee..7cd2158 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -486,7 +486,7 @@
 
     <style name="MediaOutputItemInactiveTitle">
         <item name="android:textSize">16sp</item>
-        <item name="android:textColor">@color/media_dialog_item_main_content</item>
+        <item name="android:textColor">@color/media_dialog_inactive_item_main_content</item>
     </style>
 
     <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java
index 4394ecb..98212e1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java
@@ -25,8 +25,8 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.Queue;
 import java.util.function.Consumer;
 
@@ -50,7 +50,7 @@
     private final File mTraceFile;
     private final ProtoTraceParams<P, S, T, R> mParams;
     private Choreographer mChoreographer;
-    private final Queue<T> mPool = new LinkedList<>();
+    private final Queue<T> mPool = new ArrayDeque<>();
     private final ArrayList<ProtoTraceable<R>> mTraceables = new ArrayList<>();
     private final ArrayList<ProtoTraceable<R>> mTmpTraceables = new ArrayList<>();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
index 19d39d5..d445980 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
@@ -284,6 +284,8 @@
         )
     }
 
+    private val glyphFilter: GlyphCallback? = null // Add text animation tweak here.
+
     /**
      * Set text style with an optional animation.
      *
@@ -315,6 +317,7 @@
                 delay = delay,
                 onAnimationEnd = onAnimationEnd
             )
+            textAnimator?.glyphFilter = glyphFilter
         } else {
             // when the text animator is set, update its start values
             onTextAnimatorInitialized = Runnable {
@@ -328,6 +331,7 @@
                     delay = delay,
                     onAnimationEnd = onAnimationEnd
                 )
+                textAnimator?.glyphFilter = glyphFilter
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
index 3361015..ade89af 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
@@ -22,12 +22,14 @@
 import android.animation.ValueAnimator
 import android.graphics.Canvas
 import android.graphics.Typeface
+import android.graphics.fonts.Font
 import android.text.Layout
 import android.util.SparseArray
 
 private const val TAG_WGHT = "wght"
 private const val DEFAULT_ANIMATION_DURATION: Long = 300
 
+typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
 /**
  * This class provides text animation between two styles.
  *
@@ -74,6 +76,59 @@
         })
     }
 
+    sealed class PositionedGlyph {
+
+        /**
+         * Mutable X coordinate of the glyph position relative from drawing offset.
+         */
+        var x: Float = 0f
+
+        /**
+         * Mutable Y coordinate of the glyph position relative from the baseline.
+         */
+        var y: Float = 0f
+
+        /**
+         * Mutable text size of the glyph in pixels.
+         */
+        var textSize: Float = 0f
+
+        /**
+         * Mutable color of the glyph.
+         */
+        var color: Int = 0
+
+        /**
+         * Immutable character offset in the text that the current font run start.
+         */
+        abstract var runStart: Int
+            protected set
+
+        /**
+         * Immutable run length of the font run.
+         */
+        abstract var runLength: Int
+            protected set
+
+        /**
+         * Immutable glyph index of the font run.
+         */
+        abstract var glyphIndex: Int
+            protected set
+
+        /**
+         * Immutable font instance for this font run.
+         */
+        abstract var font: Font
+            protected set
+
+        /**
+         * Immutable glyph ID for this glyph.
+         */
+        abstract var glyphId: Int
+            protected set
+    }
+
     private val typefaceCache = SparseArray<Typeface?>()
 
     fun updateLayout(layout: Layout) {
@@ -84,6 +139,57 @@
         return animator.isRunning
     }
 
+    /**
+     * GlyphFilter applied just before drawing to canvas for tweaking positions and text size.
+     *
+     * This callback is called for each glyphs just before drawing the glyphs. This function will
+     * be called with the intrinsic position, size, color, glyph ID and font instance. You can
+     * mutate the position, size and color for tweaking animations.
+     * Do not keep the reference of passed glyph object. The interpolator reuses that object for
+     * avoiding object allocations.
+     *
+     * Details:
+     * The text is drawn with font run units. The font run is a text segment that draws with the
+     * same font. The {@code runStart} and {@code runLimit} is a range of the font run in the text
+     * that current glyph is in. Once the font run is determined, the system will convert characters
+     * into glyph IDs. The {@code glyphId} is the glyph identifier in the font and
+     * {@code glyphIndex} is the offset of the converted glyph array. Please note that the
+     * {@code glyphIndex} is not a character index, because the character will not be converted to
+     * glyph one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
+     * composed from multiple characters.
+     *
+     * Here is an example of font runs: "fin. 終わり"
+     *
+     * Characters :    f      i      n      .      _      終     わ     り
+     * Code Points: \u0066 \u0069 \u006E \u002E \u0020 \u7D42 \u308F \u308A
+     * Font Runs  : <-- Roboto-Regular.ttf          --><-- NotoSans-CJK.otf -->
+     *                  runStart = 0, runLength = 5        runStart = 5, runLength = 3
+     * Glyph IDs  :      194        48     7      8     4367   1039   1002
+     * Glyph Index:       0          1     2      3       0      1      2
+     *
+     * In this example, the "fi" is converted into ligature form, thus the single glyph ID is
+     * assigned for two characters, f and i.
+     *
+     * Example:
+     * ```
+     * private val glyphFilter: GlyphCallback = { glyph, progress ->
+     *     val index = glyph.runStart
+     *     val i = glyph.glyphIndex
+     *     val moveAmount = 1.3f
+     *     val sign = (-1 + 2 * ((i + index) % 2))
+     *     val turnProgress = if (progress < .5f) progress / 0.5f else (1.0f - progress) / 0.5f
+     *
+     *     // You can modify (x, y) coordinates, textSize and color during animation.
+     *     glyph.textSize += glyph.textSize * sign * moveAmount * turnProgress
+     *     glyph.y += glyph.y * sign * moveAmount * turnProgress
+     *     glyph.x += glyph.x * sign * moveAmount * turnProgress
+     * }
+     * ```
+     */
+    var glyphFilter: GlyphCallback?
+        get() = textInterpolator.glyphFilter
+        set(value) { textInterpolator.glyphFilter = value }
+
     fun draw(c: Canvas) = textInterpolator.draw(c)
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
index 5d5797c..20dbe29 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
@@ -89,8 +89,11 @@
     private var lines = listOf<Line>()
     private val fontInterpolator = FontInterpolator()
 
-    // Recycling object for glyph drawing. Will be extended for the longest font run if needed.
-    private val tmpDrawPaint = TextPaint()
+    // Recycling object for glyph drawing and tweaking.
+    private val tmpPaint = TextPaint()
+    private val tmpPaintForGlyph by lazy { TextPaint() }
+    private val tmpGlyph by lazy { MutablePositionedGlyph() }
+    // Will be extended for the longest font run if needed.
     private var tmpPositionArray = FloatArray(20)
 
     /**
@@ -206,8 +209,8 @@
         } else if (progress == 1f) {
             basePaint.set(targetPaint)
         } else {
-            lerp(basePaint, targetPaint, progress, tmpDrawPaint)
-            basePaint.set(tmpDrawPaint)
+            lerp(basePaint, targetPaint, progress, tmpPaint)
+            basePaint.set(tmpPaint)
         }
 
         lines.forEach { line ->
@@ -231,7 +234,7 @@
      * @param canvas a canvas.
      */
     fun draw(canvas: Canvas) {
-        lerp(basePaint, targetPaint, progress, tmpDrawPaint)
+        lerp(basePaint, targetPaint, progress, tmpPaint)
         lines.forEachIndexed { lineNo, line ->
             line.runs.forEach { run ->
                 canvas.save()
@@ -241,7 +244,7 @@
                     canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat())
 
                     run.fontRuns.forEach { fontRun ->
-                        drawFontRun(canvas, run, fontRun, tmpDrawPaint)
+                        drawFontRun(canvas, run, fontRun, tmpPaint)
                     }
                 } finally {
                     canvas.restore()
@@ -330,24 +333,82 @@
         }
     }
 
+    private class MutablePositionedGlyph : TextAnimator.PositionedGlyph() {
+        override var runStart: Int = 0
+            public set
+        override var runLength: Int = 0
+            public set
+        override var glyphIndex: Int = 0
+            public set
+        override lateinit var font: Font
+            public set
+        override var glyphId: Int = 0
+            public set
+    }
+
+    var glyphFilter: GlyphCallback? = null
+
     // Draws single font run.
     private fun drawFontRun(c: Canvas, line: Run, run: FontRun, paint: Paint) {
         var arrayIndex = 0
+        val font = fontInterpolator.lerp(run.baseFont, run.targetFont, progress)
+
+        val glyphFilter = glyphFilter
+        if (glyphFilter == null) {
+            for (i in run.start until run.end) {
+                tmpPositionArray[arrayIndex++] =
+                        MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
+                tmpPositionArray[arrayIndex++] =
+                        MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+            }
+            c.drawGlyphs(line.glyphIds, run.start, tmpPositionArray, 0, run.length, font, paint)
+            return
+        }
+
+        tmpGlyph.font = font
+        tmpGlyph.runStart = run.start
+        tmpGlyph.runLength = run.end - run.start
+
+        tmpPaintForGlyph.set(paint)
+        var prevStart = run.start
+
         for (i in run.start until run.end) {
-            tmpPositionArray[arrayIndex++] =
-                    MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
-            tmpPositionArray[arrayIndex++] =
-                    MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+            tmpGlyph.glyphId = line.glyphIds[i]
+            tmpGlyph.x = MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
+            tmpGlyph.y = MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+            tmpGlyph.textSize = paint.textSize
+            tmpGlyph.color = paint.color
+
+            glyphFilter(tmpGlyph, progress)
+
+            if (tmpGlyph.textSize != paint.textSize || tmpGlyph.color != paint.color) {
+                tmpPaintForGlyph.textSize = tmpGlyph.textSize
+                tmpPaintForGlyph.color = tmpGlyph.color
+
+                c.drawGlyphs(
+                        line.glyphIds,
+                        prevStart,
+                        tmpPositionArray,
+                        0,
+                        i - prevStart,
+                        font,
+                        tmpPaintForGlyph)
+                prevStart = i
+                arrayIndex = 0
+            }
+
+            tmpPositionArray[arrayIndex++] = tmpGlyph.x
+            tmpPositionArray[arrayIndex++] = tmpGlyph.y
         }
 
         c.drawGlyphs(
                 line.glyphIds,
-                run.start,
+                prevStart,
                 tmpPositionArray,
                 0,
-                run.length,
-                fontInterpolator.lerp(run.baseFont, run.targetFont, progress),
-                paint)
+                run.end - prevStart,
+                font,
+                tmpPaintForGlyph)
     }
 
     private fun updatePositionsAndFonts(
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java
new file mode 100644
index 0000000..fd84543
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.qs.QSUserSwitcherEvent;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+
+import javax.inject.Inject;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
+/**
+ * Manages handling of guest session persistent notification
+ * and actions to reset guest or exit guest session
+ */
+public final class GuestResetOrExitSessionReceiver extends BroadcastReceiver {
+
+    private static final String TAG = GuestResetOrExitSessionReceiver.class.getSimpleName();
+
+    /**
+     * Broadcast sent to the system when guest user needs to be reset.
+     * This is only sent to registered receivers, not manifest receivers.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_GUEST_RESET = "android.intent.action.GUEST_RESET";
+
+    /**
+     * Broadcast sent to the system when guest user needs to exit.
+     * This is only sent to registered receivers, not manifest receivers.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_GUEST_EXIT = "android.intent.action.GUEST_EXIT";
+
+    public AlertDialog mExitSessionDialog;
+    public AlertDialog mResetSessionDialog;
+    private final UserTracker mUserTracker;
+    private final BroadcastDispatcher mBroadcastDispatcher;
+    private final ResetSessionDialog.Factory mResetSessionDialogFactory;
+    private final ExitSessionDialog.Factory mExitSessionDialogFactory;
+
+    @Inject
+    public GuestResetOrExitSessionReceiver(UserTracker userTracker,
+            BroadcastDispatcher broadcastDispatcher,
+            ResetSessionDialog.Factory resetSessionDialogFactory,
+            ExitSessionDialog.Factory exitSessionDialogFactory) {
+        mUserTracker = userTracker;
+        mBroadcastDispatcher = broadcastDispatcher;
+        mResetSessionDialogFactory = resetSessionDialogFactory;
+        mExitSessionDialogFactory = exitSessionDialogFactory;
+    }
+
+    /**
+     * Register this receiver with the {@link BroadcastDispatcher}
+     */
+    public void register() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_GUEST_RESET);
+        intentFilter.addAction(ACTION_GUEST_EXIT);
+        mBroadcastDispatcher.registerReceiver(this, intentFilter, null /* handler */,
+                                             UserHandle.SYSTEM);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+
+        cancelResetDialog();
+        cancelExitDialog();
+
+        UserInfo currentUser = mUserTracker.getUserInfo();
+        if (!currentUser.isGuest()) {
+            return;
+        }
+
+        if (ACTION_GUEST_RESET.equals(action)) {
+            mResetSessionDialog = mResetSessionDialogFactory.create(currentUser.id);
+            mResetSessionDialog.show();
+        } else if (ACTION_GUEST_EXIT.equals(action)) {
+            mExitSessionDialog = mExitSessionDialogFactory.create(currentUser.id,
+                        currentUser.isEphemeral());
+            mExitSessionDialog.show();
+        }
+    }
+
+    private void cancelResetDialog() {
+        if (mResetSessionDialog != null && mResetSessionDialog.isShowing()) {
+            mResetSessionDialog.cancel();
+            mResetSessionDialog = null;
+        }
+    }
+
+    private void cancelExitDialog() {
+        if (mExitSessionDialog != null && mExitSessionDialog.isShowing()) {
+            mExitSessionDialog.cancel();
+            mExitSessionDialog = null;
+        }
+    }
+
+    /**
+     * Dialog shown when asking for confirmation before
+     * reset and restart of guest user.
+     */
+    public static final class ResetSessionDialog extends SystemUIDialog implements
+            DialogInterface.OnClickListener {
+
+        private final UserSwitcherController mUserSwitcherController;
+        private final UiEventLogger mUiEventLogger;
+        private final int mUserId;
+
+        /** Factory class to create guest reset dialog instance */
+        @AssistedFactory
+        public interface Factory {
+            /** Create a guest reset dialog instance */
+            ResetSessionDialog create(int userId);
+        }
+
+        @AssistedInject
+        ResetSessionDialog(Context context,
+                UserSwitcherController userSwitcherController,
+                UiEventLogger uiEventLogger,
+                @Assisted int userId) {
+            super(context);
+
+            setTitle(com.android.settingslib.R.string.guest_reset_and_restart_dialog_title);
+            setMessage(context.getString(
+                        com.android.settingslib.R.string.guest_reset_and_restart_dialog_message));
+            setButton(DialogInterface.BUTTON_NEUTRAL,
+                    context.getString(android.R.string.cancel), this);
+            setButton(DialogInterface.BUTTON_POSITIVE,
+                    context.getString(
+                        com.android.settingslib.R.string.guest_reset_guest_confirm_button), this);
+            setCanceledOnTouchOutside(false);
+
+            mUserSwitcherController = userSwitcherController;
+            mUiEventLogger = uiEventLogger;
+            mUserId = userId;
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            if (which == DialogInterface.BUTTON_POSITIVE) {
+                mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
+                mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL);
+            } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+                cancel();
+            }
+        }
+    }
+
+    /**
+     * Dialog shown when asking for confirmation before
+     * exit of guest user.
+     */
+    public static final class ExitSessionDialog extends SystemUIDialog implements
+            DialogInterface.OnClickListener {
+
+        private final UserSwitcherController mUserSwitcherController;
+        private final int mUserId;
+        private boolean mIsEphemeral;
+
+        /** Factory class to create guest exit dialog instance */
+        @AssistedFactory
+        public interface Factory {
+            /** Create a guest exit dialog instance */
+            ExitSessionDialog create(int userId, boolean isEphemeral);
+        }
+
+        @AssistedInject
+        ExitSessionDialog(Context context,
+                UserSwitcherController userSwitcherController,
+                @Assisted int userId,
+                @Assisted boolean isEphemeral) {
+            super(context);
+
+            if (isEphemeral) {
+                setTitle(context.getString(
+                            com.android.settingslib.R.string.guest_exit_dialog_title));
+                setMessage(context.getString(
+                            com.android.settingslib.R.string.guest_exit_dialog_message));
+                setButton(DialogInterface.BUTTON_NEUTRAL,
+                        context.getString(android.R.string.cancel), this);
+                setButton(DialogInterface.BUTTON_POSITIVE,
+                        context.getString(
+                            com.android.settingslib.R.string.guest_exit_dialog_button), this);
+            } else {
+                setTitle(context.getString(
+                            com.android.settingslib
+                                .R.string.guest_exit_dialog_title_non_ephemeral));
+                setMessage(context.getString(
+                            com.android.settingslib
+                                .R.string.guest_exit_dialog_message_non_ephemeral));
+                setButton(DialogInterface.BUTTON_NEUTRAL,
+                        context.getString(android.R.string.cancel), this);
+                setButton(DialogInterface.BUTTON_NEGATIVE,
+                        context.getString(
+                            com.android.settingslib.R.string.guest_exit_clear_data_button), this);
+                setButton(DialogInterface.BUTTON_POSITIVE,
+                        context.getString(
+                            com.android.settingslib.R.string.guest_exit_save_data_button), this);
+            }
+            setCanceledOnTouchOutside(false);
+
+            mUserSwitcherController = userSwitcherController;
+            mUserId = userId;
+            mIsEphemeral = isEphemeral;
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            if (mIsEphemeral) {
+                if (which == DialogInterface.BUTTON_POSITIVE) {
+                    // Ephemeral guest: exit guest, guest is removed by the system
+                    // on exit, since its marked ephemeral
+                    mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false);
+                } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+                    // Cancel clicked, do nothing
+                    cancel();
+                }
+            } else {
+                if (which == DialogInterface.BUTTON_POSITIVE) {
+                    // Non-ephemeral guest: exit guest, guest is not removed by the system
+                    // on exit, since its marked non-ephemeral
+                    mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false);
+                } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+                    // Non-ephemeral guest: remove guest and then exit
+                    mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, true);
+                } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+                    // Cancel clicked, do nothing
+                    cancel();
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 9a6020f..76a7cad 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -35,12 +35,18 @@
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.util.settings.SecureSettings;
 
+import javax.inject.Inject;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
 /**
  * Manages notification when a guest session is resumed.
  */
 public class GuestResumeSessionReceiver extends BroadcastReceiver {
 
-    private static final String TAG = "GuestResumeSessionReceiver";
+    private static final String TAG = GuestResumeSessionReceiver.class.getSimpleName();
 
     @VisibleForTesting
     public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
@@ -48,27 +54,31 @@
     @VisibleForTesting
     public AlertDialog mNewSessionDialog;
     private final UserTracker mUserTracker;
-    private final UserSwitcherController mUserSwitcherController;
-    private final UiEventLogger mUiEventLogger;
     private final SecureSettings mSecureSettings;
+    private final BroadcastDispatcher mBroadcastDispatcher;
+    private final ResetSessionDialog.Factory mResetSessionDialogFactory;
+    private final GuestSessionNotification mGuestSessionNotification;
 
-    public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController,
-            UserTracker userTracker, UiEventLogger uiEventLogger,
-            SecureSettings secureSettings) {
-        mUserSwitcherController = userSwitcherController;
+    @Inject
+    public GuestResumeSessionReceiver(
+            UserTracker userTracker,
+            SecureSettings secureSettings,
+            BroadcastDispatcher broadcastDispatcher,
+            GuestSessionNotification guestSessionNotification,
+            ResetSessionDialog.Factory resetSessionDialogFactory) {
         mUserTracker = userTracker;
-        mUiEventLogger = uiEventLogger;
         mSecureSettings = secureSettings;
+        mBroadcastDispatcher = broadcastDispatcher;
+        mGuestSessionNotification = guestSessionNotification;
+        mResetSessionDialogFactory = resetSessionDialogFactory;
     }
 
     /**
      * Register this receiver with the {@link BroadcastDispatcher}
-     *
-     * @param broadcastDispatcher to register the receiver.
      */
-    public void register(BroadcastDispatcher broadcastDispatcher) {
+    public void register() {
         IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-        broadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM);
+        mBroadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM);
     }
 
     @Override
@@ -89,14 +99,25 @@
                 return;
             }
 
-            int notFirstLogin = mSecureSettings.getIntForUser(
+            int guestLoginState = mSecureSettings.getIntForUser(
                     SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
-            if (notFirstLogin != 0) {
-                mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController,
-                        mUiEventLogger, userId);
+
+            if (guestLoginState == 0) {
+                // set 1 to indicate, 1st login
+                guestLoginState = 1;
+                mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
+            } else if (guestLoginState == 1) {
+                // set 2 to indicate, 2nd or later login
+                guestLoginState = 2;
+                mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
+            }
+
+            mGuestSessionNotification.createPersistentNotification(currentUser,
+                                                                   (guestLoginState <= 1));
+
+            if (guestLoginState > 1) {
+                mNewSessionDialog = mResetSessionDialogFactory.create(userId);
                 mNewSessionDialog.show();
-            } else {
-                mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
             }
         }
     }
@@ -124,10 +145,19 @@
         private final UiEventLogger mUiEventLogger;
         private final int mUserId;
 
-        ResetSessionDialog(Context context,
+
+        /** Factory class to create guest reset dialog instance */
+        @AssistedFactory
+        public interface Factory {
+            /** Create a guest reset dialog instance */
+            ResetSessionDialog create(int userId);
+        }
+
+        @AssistedInject
+        public ResetSessionDialog(Context context,
                 UserSwitcherController userSwitcherController,
                 UiEventLogger uiEventLogger,
-                int userId) {
+                @Assisted int userId) {
             super(context, false /* dismissOnDeviceLock */);
 
             setTitle(context.getString(R.string.guest_wipe_session_title));
diff --git a/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
new file mode 100644
index 0000000..b0eaab9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.FeatureFlagUtils;
+
+import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.util.NotificationChannels;
+
+import javax.inject.Inject;
+
+/**
+ * Posts a persistent notification on entry to guest mode
+ */
+public final class GuestSessionNotification {
+
+    private static final String TAG = GuestSessionNotification.class.getSimpleName();
+
+    private final Context mContext;
+    private final NotificationManager mNotificationManager;
+
+    @Inject
+    public GuestSessionNotification(Context context,
+            NotificationManager notificationManager) {
+        mContext = context;
+        mNotificationManager = notificationManager;
+    }
+
+    private void overrideNotificationAppName(Notification.Builder notificationBuilder) {
+        final Bundle extras = new Bundle();
+        String appName = mContext.getString(R.string.guest_notification_app_name);
+
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);
+
+        notificationBuilder.addExtras(extras);
+    }
+
+    void createPersistentNotification(UserInfo userInfo, boolean isGuestFirstLogin) {
+        if (!FeatureFlagUtils.isEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES)
+                || !userInfo.isGuest()) {
+            // we create a persistent notification only if enabled and only for guests
+            return;
+        }
+        String contentText;
+        if (userInfo.isEphemeral()) {
+            contentText = mContext.getString(R.string.guest_notification_ephemeral);
+        } else if (isGuestFirstLogin) {
+            contentText = mContext.getString(R.string.guest_notification_non_ephemeral);
+        } else {
+            contentText = mContext.getString(
+                            R.string.guest_notification_non_ephemeral_non_first_login);
+        }
+
+        final Intent guestExitIntent = new Intent(
+                        GuestResetOrExitSessionReceiver.ACTION_GUEST_EXIT);
+        final Intent userSettingsIntent = new Intent(Settings.ACTION_USER_SETTINGS);
+
+        PendingIntent guestExitPendingIntent =
+                PendingIntent.getBroadcastAsUser(mContext, 0, guestExitIntent,
+                    PendingIntent.FLAG_IMMUTABLE,
+                    UserHandle.SYSTEM);
+
+        PendingIntent userSettingsPendingIntent =
+                PendingIntent.getActivityAsUser(mContext, 0, userSettingsIntent,
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+                    null,
+                    UserHandle.of(userInfo.id));
+
+        Notification.Builder builder = new Notification.Builder(mContext,
+                                                                NotificationChannels.ALERTS)
+                .setSmallIcon(R.drawable.ic_account_circle)
+                .setContentTitle(mContext.getString(R.string.guest_notification_session_active))
+                .setContentText(contentText)
+                .setPriority(Notification.PRIORITY_DEFAULT)
+                .setOngoing(true)
+                .setContentIntent(userSettingsPendingIntent);
+
+        // we show reset button only if this is a 2nd or later login
+        if (!isGuestFirstLogin) {
+            final Intent guestResetIntent = new Intent(
+                            GuestResetOrExitSessionReceiver.ACTION_GUEST_RESET);
+
+            PendingIntent guestResetPendingIntent =
+                    PendingIntent.getBroadcastAsUser(mContext, 0, guestResetIntent,
+                        PendingIntent.FLAG_IMMUTABLE,
+                        UserHandle.SYSTEM);
+
+            builder.addAction(R.drawable.ic_sysbar_home,
+                        mContext.getString(
+                            com.android.settingslib.R.string.guest_reset_guest_confirm_button),
+                        guestResetPendingIntent);
+        }
+        builder.addAction(R.drawable.ic_sysbar_home,
+                        mContext.getString(
+                            com.android.settingslib.R.string.guest_exit_button),
+                        guestExitPendingIntent);
+
+        overrideNotificationAppName(builder);
+
+        mNotificationManager.notifyAsUser(null,
+                                         SystemMessageProto.SystemMessage.NOTE_GUEST_SESSION,
+                                         builder.build(),
+                                         UserHandle.of(userInfo.id));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index ff5715c..9aa5fae 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -63,7 +63,6 @@
             Key.QS_WORK_ADDED,
             Key.QS_NIGHTDISPLAY_ADDED,
             Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
-            Key.SEEN_MULTI_USER,
             Key.SEEN_RINGER_GUIDANCE_COUNT,
             Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
             Key.TOUCHED_RINGER_TOGGLE,
@@ -106,7 +105,6 @@
          * Settings panel.
          */
         String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount";
-        String SEEN_MULTI_USER = "HasSeenMultiUser";
         String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount";
         String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
         String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
index e5da389..4773f2a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java
@@ -18,9 +18,9 @@
 
 import android.view.MotionEvent;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
 
@@ -33,13 +33,13 @@
  */
 public class TimeLimitedMotionEventBuffer implements List<MotionEvent> {
 
-    private final LinkedList<MotionEvent> mMotionEvents;
+    private final List<MotionEvent> mMotionEvents;
     private final long mMaxAgeMs;
 
     public TimeLimitedMotionEventBuffer(long maxAgeMs) {
         super();
         mMaxAgeMs = maxAgeMs;
-        mMotionEvents = new LinkedList<>();
+        mMotionEvents = new ArrayList<>();
     }
 
     private void ejectOldEvents() {
@@ -47,7 +47,7 @@
             return;
         }
         Iterator<MotionEvent> iter = listIterator();
-        long mostRecentMs = mMotionEvents.getLast().getEventTime();
+        long mostRecentMs = mMotionEvents.get(mMotionEvents.size() - 1).getEventTime();
         while (iter.hasNext()) {
             MotionEvent ev = iter.next();
             if (mostRecentMs - ev.getEventTime() > mMaxAgeMs) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index bed553e..2ea5967 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -33,6 +33,7 @@
 import android.util.ArrayMap
 import android.util.Log
 import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID
 import com.android.systemui.Dumpable
 import com.android.systemui.backup.BackupHelper
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -43,6 +44,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index f9115b2..3eb58bb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -343,7 +343,7 @@
         info.className = Switch::class.java.name
     }
 
-    override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
+    override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean {
         if (super.performAccessibilityAction(host, action, args)) {
             return true
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 0cf3333..8ba6f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -18,6 +18,8 @@
 
 import android.content.BroadcastReceiver;
 
+import com.android.systemui.GuestResetOrExitSessionReceiver;
+import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
 import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
 import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -89,4 +91,21 @@
     public abstract BroadcastReceiver bindPeopleSpaceWidgetProvider(
             PeopleSpaceWidgetProvider broadcastReceiver);
 
+    /**
+     *
+     */
+    @Binds
+    @IntoMap
+    @ClassKey(GuestResumeSessionReceiver.class)
+    public abstract BroadcastReceiver bindGuestResumeSessionReceiver(
+            GuestResumeSessionReceiver broadcastReceiver);
+
+    /**
+     *
+     */
+    @Binds
+    @IntoMap
+    @ClassKey(GuestResetOrExitSessionReceiver.class)
+    public abstract BroadcastReceiver bindGuestResetOrExitSessionReceiver(
+            GuestResetOrExitSessionReceiver broadcastReceiver);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 5d154c3..c870b89 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -178,7 +178,8 @@
             KeyguardBypassController bypassController,
             GroupMembershipManager groupManager,
             VisualStabilityProvider visualStabilityProvider,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            @Main Handler handler) {
         return new HeadsUpManagerPhone(
                 context,
                 headsUpManagerLogger,
@@ -186,7 +187,8 @@
                 bypassController,
                 groupManager,
                 visualStabilityProvider,
-                configurationController
+                configurationController,
+                handler
         );
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index 5aedbdc..bd00ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -34,8 +34,8 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -71,7 +71,7 @@
     @Nullable private ShowNextIndication mShowNextIndicationRunnable;
 
     // List of indication types to show. The next indication to show is always at index 0
-    private final List<Integer> mIndicationQueue = new LinkedList<>();
+    private final List<Integer> mIndicationQueue = new ArrayList<>();
     private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE;
     private CharSequence mCurrMessage;
     private long mLastIndicationSwitch;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index d37f3ae..547d13c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -611,12 +611,11 @@
 
     private void bindActionButtons(MediaData data) {
         MediaButton semanticActions = data.getSemanticActions();
-        ImageButton[] genericButtons = new ImageButton[]{
-                mMediaViewHolder.getAction0(),
-                mMediaViewHolder.getAction1(),
-                mMediaViewHolder.getAction2(),
-                mMediaViewHolder.getAction3(),
-                mMediaViewHolder.getAction4()};
+
+        List<ImageButton> genericButtons = new ArrayList<>();
+        for (int id : MediaViewHolder.Companion.getGenericButtonIds()) {
+            genericButtons.add(mMediaViewHolder.getAction(id));
+        }
 
         ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
         ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
@@ -643,19 +642,19 @@
             List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
             List<MediaAction> actions = data.getActions();
             int i = 0;
-            for (; i < actions.size(); i++) {
+            for (; i < actions.size() && i < genericButtons.size(); i++) {
                 boolean showInCompact = actionsWhenCollapsed.contains(i);
                 setGenericButton(
-                        genericButtons[i],
+                        genericButtons.get(i),
                         actions.get(i),
                         collapsedSet,
                         expandedSet,
                         showInCompact);
             }
-            for (; i < 5; i++) {
+            for (; i < genericButtons.size(); i++) {
                 // Hide any unused buttons
                 setGenericButton(
-                        genericButtons[i],
+                        genericButtons.get(i),
                         /* mediaAction= */ null,
                         collapsedSet,
                         expandedSet,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index ad93855..9a2977d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -161,6 +161,10 @@
         @JvmField
         val MAX_COMPACT_ACTIONS = 3
 
+        // Maximum number of actions allowed in expanded view
+        @JvmField
+        val MAX_NOTIFICATION_ACTIONS = MediaViewHolder.genericButtonIds.size
+
         /** Maximum number of [PlaybackState.CustomAction] buttons supported */
         @JvmField
         val MAX_CUSTOM_ACTIONS = 4
@@ -727,6 +731,11 @@
 
         if (actions != null) {
             for ((index, action) in actions.withIndex()) {
+                if (index == MAX_NOTIFICATION_ACTIONS) {
+                    Log.w(TAG, "Too many notification actions for ${sbn.key}," +
+                        " limiting to first $MAX_NOTIFICATION_ACTIONS")
+                    break
+                }
                 if (action.getIcon() == null) {
                     if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
                     actionsToShowCollapsed.remove(index)
@@ -843,18 +852,23 @@
     }
 
     /**
-     * Get a [MediaAction] representing one of
-     * - [PlaybackState.ACTION_PLAY]
-     * - [PlaybackState.ACTION_PAUSE]
-     * - [PlaybackState.ACTION_SKIP_TO_PREVIOUS]
-     * - [PlaybackState.ACTION_SKIP_TO_NEXT]
+     * Create a [MediaAction] for a given action and media session
+     *
+     * @param controller MediaController for the session
+     * @param stateActions The actions included with the session's [PlaybackState]
+     * @param action A [PlaybackState.Actions] value representing what action to generate. One of:
+     *      [PlaybackState.ACTION_PLAY]
+     *      [PlaybackState.ACTION_PAUSE]
+     *      [PlaybackState.ACTION_SKIP_TO_PREVIOUS]
+     *      [PlaybackState.ACTION_SKIP_TO_NEXT]
+     * @return A [MediaAction] with correct values set, or null if the state doesn't support it
      */
     private fun getStandardAction(
         controller: MediaController,
         stateActions: Long,
-        action: Long
+        @PlaybackState.Actions action: Long
     ): MediaAction? {
-        if (stateActions and action == 0L) {
+        if (!includesAction(stateActions, action)) {
             return null
         }
 
@@ -896,6 +910,17 @@
     }
 
     /**
+     * Check whether the actions from a [PlaybackState] include a specific action
+     */
+    private fun includesAction(stateActions: Long, @PlaybackState.Actions action: Long): Boolean {
+        if ((action == PlaybackState.ACTION_PLAY || action == PlaybackState.ACTION_PAUSE) &&
+                (stateActions and PlaybackState.ACTION_PLAY_PAUSE > 0L)) {
+            return true
+        }
+        return (stateActions and action != 0L)
+    }
+
+    /**
      * Get a [MediaAction] representing a [PlaybackState.CustomAction]
      */
     private fun getCustomAction(
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 7f25642..5e810f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.Utils
 import com.android.systemui.util.time.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
index 34a77f2..8964d71 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
@@ -178,5 +178,14 @@
                 R.id.dismiss,
                 R.id.settings
         )
+
+        // Buttons used for notification-based actions
+        val genericButtonIds = setOf(
+            R.id.action0,
+            R.id.action1,
+            R.id.action2,
+            R.id.action3,
+            R.id.action4
+        )
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 0edadcc..9e4ee61 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -118,9 +118,7 @@
             mCheckBox.setVisibility(View.GONE);
             mStatusIcon.setVisibility(View.GONE);
             mContainerLayout.setOnClickListener(null);
-            mTitleText.setTextColor(mController.getColorItemContent());
-            mSubTitleText.setTextColor(mController.getColorItemContent());
-            mTwoLineTitleText.setTextColor(mController.getColorItemContent());
+            mTitleText.setTextColor(mController.getColorInactiveItem());
             mSeekBar.getProgressDrawable().setColorFilter(
                     new PorterDuffColorFilter(mController.getColorSeekbarProgress(),
                             PorterDuff.Mode.SRC_IN));
@@ -141,7 +139,7 @@
                         && !mController.hasAdjustVolumeUserRestriction()) {
                     mProgressBar.getIndeterminateDrawable().setColorFilter(
                             new PorterDuffColorFilter(
-                                    mController.getColorItemContent(),
+                                    mController.getColorInactiveItem(),
                                     PorterDuff.Mode.SRC_IN));
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             false /* showSeekBar*/,
@@ -156,7 +154,7 @@
                     mTitleIcon.setAlpha(DEVICE_CONNECTED_ALPHA);
                     mStatusIcon.setImageDrawable(
                             mContext.getDrawable(R.drawable.media_output_status_failed));
-                    mStatusIcon.setColorFilter(mController.getColorItemContent());
+                    mStatusIcon.setColorFilter(mController.getColorInactiveItem());
                     setTwoLineLayout(device, false /* bFocused */,
                             false /* showSeekBar */, false /* showProgressBar */,
                             true /* showSubtitle */, true /* showStatus */);
@@ -164,34 +162,40 @@
                     mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
                 } else if (mController.getSelectedMediaDevice().size() > 1
                         && isDeviceIncluded(mController.getSelectedMediaDevice(), device)) {
-                    mTitleText.setTextColor(mController.getColorItemContent());
+                    mTitleText.setTextColor(mController.getColorActiveItem());
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             true /* showSeekBar */,
                             false /* showProgressBar */, false /* showStatus */);
+                    mCheckBox.setOnCheckedChangeListener(null);
                     mCheckBox.setVisibility(View.VISIBLE);
                     mCheckBox.setChecked(true);
-                    mSeekBar.setOnClickListener(null);
-                    mSeekBar.setOnClickListener(v -> onGroupActionTriggered(false, device));
-                    setCheckBoxColor(mCheckBox, mController.getColorItemContent());
+                    mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+                        onCheckBoxClicked(false, device);
+                    });
+                    setCheckBoxColor(mCheckBox, mController.getColorActiveItem());
                     initSeekbar(device);
                 } else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
                     mStatusIcon.setImageDrawable(
                             mContext.getDrawable(R.drawable.media_output_status_check));
-                    mStatusIcon.setColorFilter(mController.getColorItemContent());
-                    mTitleText.setTextColor(mController.getColorItemContent());
+                    mStatusIcon.setColorFilter(mController.getColorActiveItem());
+                    mTitleText.setTextColor(mController.getColorActiveItem());
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             true /* showSeekBar */,
                             false /* showProgressBar */, true /* showStatus */);
                     initSeekbar(device);
                     mCurrentActivePosition = position;
                 } else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+                    mCheckBox.setOnCheckedChangeListener(null);
                     mCheckBox.setVisibility(View.VISIBLE);
                     mCheckBox.setChecked(false);
-                    mContainerLayout.setOnClickListener(v -> onGroupActionTriggered(true, device));
-                    setCheckBoxColor(mCheckBox, mController.getColorItemContent());
+                    mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+                        onCheckBoxClicked(true, device);
+                    });
+                    setCheckBoxColor(mCheckBox, mController.getColorInactiveItem());
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */,
                             false /* showSeekBar */,
                             false /* showProgressBar */, false /* showStatus */);
+                    mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
                 } else {
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
                     mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
@@ -209,7 +213,7 @@
         @Override
         void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
             if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
-                mTitleText.setTextColor(mController.getColorItemContent());
+                mTitleText.setTextColor(mController.getColorInactiveItem());
                 mCheckBox.setVisibility(View.GONE);
                 setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
                         false /* bFocused */);
@@ -221,7 +225,7 @@
             }
         }
 
-        private void onGroupActionTriggered(boolean isChecked, MediaDevice device) {
+        private void onCheckBoxClicked(boolean isChecked, MediaDevice device) {
             if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
                 mController.addDeviceToPlayMedia(device);
             } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 5c536d4..958e9ed 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -183,16 +183,14 @@
         void setSingleLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
                 boolean showProgressBar, boolean showStatus) {
             mTwoLineLayout.setVisibility(View.GONE);
-            boolean isActive = showSeekBar || showProgressBar;
             final Drawable backgroundDrawable =
-                    isActive
+                    showSeekBar || showProgressBar
                             ? mContext.getDrawable(R.drawable.media_output_item_background_active)
                                     .mutate() : mContext.getDrawable(
                             R.drawable.media_output_item_background)
                             .mutate();
             backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
-                    isActive ? mController.getColorConnectedItemBackground()
-                            : mController.getColorItemBackground(),
+                    mController.getColorItemBackground(),
                     PorterDuff.Mode.SRC_IN));
             mItemLayout.setBackground(backgroundDrawable);
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
@@ -381,7 +379,7 @@
                     .mutate();
             drawable.setColorFilter(
                     new PorterDuffColorFilter(Utils.getColorStateListDefaultColor(mContext,
-                            R.color.media_dialog_item_main_content),
+                            R.color.media_dialog_active_item_main_content),
                             PorterDuff.Mode.SRC_IN));
             return drawable;
         }
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 bde772d..79be916 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -218,9 +218,10 @@
                 ColorFilter buttonColorFilter = new PorterDuffColorFilter(
                         mAdapter.getController().getColorButtonBackground(),
                         PorterDuff.Mode.SRC_IN);
+                ColorFilter onlineButtonColorFilter = new PorterDuffColorFilter(
+                        mAdapter.getController().getColorInactiveItem(), PorterDuff.Mode.SRC_IN);
                 mDoneButton.getBackground().setColorFilter(buttonColorFilter);
-                mStopButton.getBackground().setColorFilter(buttonColorFilter);
-                mDoneButton.setTextColor(mAdapter.getController().getColorPositiveButtonText());
+                mStopButton.getBackground().setColorFilter(onlineButtonColorFilter);
             }
             mHeaderIcon.setVisibility(View.VISIBLE);
             mHeaderIcon.setImageIcon(icon);
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 ec2a950..af52a2f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -117,12 +117,11 @@
 
     private MediaOutputMetricLogger mMetricLogger;
 
-    private int mColorItemContent;
+    private int mColorActiveItem;
+    private int mColorInactiveItem;
     private int mColorSeekbarProgress;
     private int mColorButtonBackground;
     private int mColorItemBackground;
-    private int mColorConnectedItemBackground;
-    private int mColorPositiveButtonText;
 
     public enum BroadcastNotifyDialog {
         ACTION_FIRST_LAUNCH,
@@ -147,18 +146,16 @@
         mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mNearbyMediaDevicesManager = nearbyMediaDevicesManagerOptional.orElse(null);
-        mColorItemContent = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_item_main_content);
+        mColorActiveItem = Utils.getColorStateListDefaultColor(mContext,
+                R.color.media_dialog_active_item_main_content);
+        mColorInactiveItem = Utils.getColorStateListDefaultColor(mContext,
+                R.color.media_dialog_inactive_item_main_content);
         mColorSeekbarProgress = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_seekbar_progress);
+                android.R.color.system_accent1_200);
         mColorButtonBackground = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_button_background);
-        mColorItemBackground = Utils.getColorStateListDefaultColor(mContext,
                 R.color.media_dialog_item_background);
-        mColorConnectedItemBackground = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_connected_item_background);
-        mColorPositiveButtonText = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_solid_button_text);
+        mColorItemBackground = Utils.getColorStateListDefaultColor(mContext,
+                android.R.color.system_accent2_50);
     }
 
     void start(@NonNull Callback cb) {
@@ -338,7 +335,8 @@
     }
 
     void setColorFilter(Drawable drawable, boolean isActive) {
-        drawable.setColorFilter(new PorterDuffColorFilter(mColorItemContent,
+        drawable.setColorFilter(new PorterDuffColorFilter(isActive
+                ? mColorActiveItem : mColorInactiveItem,
                 PorterDuff.Mode.SRC_IN));
     }
 
@@ -373,32 +371,26 @@
         ColorScheme mCurrentColorScheme = new ColorScheme(wallpaperColors,
                 isDarkTheme);
         if (isDarkTheme) {
-            mColorItemContent = mCurrentColorScheme.getAccent1().get(2); // A1-100
-            mColorSeekbarProgress = mCurrentColorScheme.getAccent2().get(7); // A2-600
-            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(4); // A1-300
-            mColorItemBackground = mCurrentColorScheme.getNeutral2().get(9); // N2-800
-            mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().get(9); // A2-800
-            mColorPositiveButtonText = mCurrentColorScheme.getAccent2().get(9); // A2-800
+            mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10);
+            mColorInactiveItem = mCurrentColorScheme.getNeutral1().get(10);
+            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(2);
+            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(2);
+            mColorItemBackground = mCurrentColorScheme.getAccent2().get(0);
         } else {
-            mColorItemContent = mCurrentColorScheme.getAccent1().get(9); // A1-800
-            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(4); // A1-300
-            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(7); // A1-600
-            mColorItemBackground = mCurrentColorScheme.getAccent2().get(1); // A2-50
-            mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().get(2); // A1-100
-            mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().get(1); // N1-50
+            mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10);
+            mColorInactiveItem = mCurrentColorScheme.getAccent1().get(7);
+            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(3);
+            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(3);
+            mColorItemBackground = mCurrentColorScheme.getAccent2().get(0);
         }
     }
 
-    public int getColorConnectedItemBackground() {
-        return mColorConnectedItemBackground;
+    public int getColorActiveItem() {
+        return mColorActiveItem;
     }
 
-    public int getColorPositiveButtonText() {
-        return mColorPositiveButtonText;
-    }
-
-    public int getColorItemContent() {
-        return mColorItemContent;
+    public int getColorInactiveItem() {
+        return mColorInactiveItem;
     }
 
     public int getColorSeekbarProgress() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java
deleted file mode 100644
index 0a3c5bf..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.dialog;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.SeekBar;
-
-/**
- * Customized seekbar used by MediaOutputDialog, which only changes progress when dragging,
- * otherwise performs click.
- */
-public class MediaOutputSeekbar extends SeekBar {
-    private static final int DRAGGING_THRESHOLD = 20;
-    private boolean mIsDragging = false;
-
-    public MediaOutputSeekbar(Context context) {
-        super(context);
-    }
-
-    public MediaOutputSeekbar(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        int width = getWidth()
-                - getPaddingLeft()
-                - getPaddingRight();
-        int thumbPos = getPaddingLeft()
-                + width
-                * getProgress()
-                / getMax();
-        if (event.getAction() == MotionEvent.ACTION_DOWN
-                && Math.abs(event.getX() - thumbPos) < DRAGGING_THRESHOLD) {
-            mIsDragging = true;
-            super.onTouchEvent(event);
-        } else if (event.getAction() == MotionEvent.ACTION_MOVE && mIsDragging) {
-            super.onTouchEvent(event);
-        } else if (event.getAction() == MotionEvent.ACTION_UP && mIsDragging) {
-            mIsDragging = false;
-            super.onTouchEvent(event);
-        } else if (event.getAction() == MotionEvent.ACTION_UP && !mIsDragging) {
-            performClick();
-        }
-        return true;
-    }
-
-    @Override
-    public boolean performClick() {
-        return super.performClick();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
index a0e803f..40ea1e6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media.taptotransfer.receiver
 
 import android.app.StatusBarManager
+import android.util.Log
 import com.android.internal.logging.UiEventLogger
 
 /**
@@ -25,15 +26,15 @@
  */
 enum class ChipStateReceiver(
     @StatusBarManager.MediaTransferSenderState val stateInt: Int,
-    val uiEvent: UiEventLogger.UiEventEnum,
+    val uiEvent: UiEventLogger.UiEventEnum
 ) {
     CLOSE_TO_SENDER(
         StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
-        MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER,
+        MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER
     ),
     FAR_FROM_SENDER(
         StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
-        MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER,
+        MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER
     );
 
     companion object {
@@ -43,8 +44,13 @@
          */
         fun getReceiverStateFromId(
             @StatusBarManager.MediaTransferReceiverState displayState: Int
-        ) : ChipStateReceiver = values().first { it.stateInt == displayState }
-
+        ): ChipStateReceiver? =
+        try {
+            values().first { it.stateInt == displayState }
+        } catch (e: NoSuchElementException) {
+            Log.e(TAG, "Could not find requested state $displayState", e)
+            null
+        }
 
         /**
          * Returns the state int from [StatusBarManager] associated with the given sender state
@@ -56,3 +62,5 @@
         fun getReceiverStateIdFromName(name: String): Int = valueOf(name).stateInt
     }
 }
+
+private const val TAG = "ChipStateReceiver"
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 5510eb1..09c4cb7 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -57,7 +57,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -126,7 +125,7 @@
     private final Runnable mCollapseRunnable = this::collapseChip;
 
     private final Runnable mAccessibilityRunnable = this::makeAccessibilityAnnouncement;
-    private final List<PrivacyItem> mItemsBeforeLastAnnouncement = new LinkedList<>();
+    private final List<PrivacyItem> mItemsBeforeLastAnnouncement = new ArrayList<>();
 
     @State
     private int mState = STATE_NOT_SHOWN;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 3a6248b..6b79a28 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -30,9 +30,9 @@
 import androidx.annotation.WorkerThread
 import com.android.systemui.Dumpable
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE
 import com.android.systemui.util.Assert
 import java.io.PrintWriter
-import java.lang.IllegalStateException
 import java.lang.ref.WeakReference
 import java.util.concurrent.Executor
 import kotlin.properties.ReadWriteProperty
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 6cfbb43..1ffa6f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -19,13 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityEvent;
 
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
@@ -43,8 +42,9 @@
     protected final ArrayMap<String, AlertEntry> mAlertEntries = new ArrayMap<>();
     protected final HeadsUpManagerLogger mLogger;
 
-    public AlertingNotificationManager(HeadsUpManagerLogger logger) {
+    public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler) {
         mLogger = logger;
+        mHandler = handler;
     }
 
     /**
@@ -57,8 +57,7 @@
     protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
     protected int mMinimumDisplayTime;
     protected int mAutoDismissNotificationDecay;
-    @VisibleForTesting
-    public Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Handler mHandler;
 
     /**
      * Called when posting a new notification that should alert the user and appear on screen.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index aac5b8d..4895fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -19,12 +19,12 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
 
 import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 57fd197..5ac4813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.SectionClassifier;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -28,6 +27,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
 import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
@@ -51,7 +51,7 @@
     public static final boolean SHOW_ALL_SECTIONS = false;
     private final StatusBarStateController mStatusBarStateController;
     private final HighPriorityProvider mHighPriorityProvider;
-    private final SectionClassifier mSectionClassifier;
+    private final SectionStyleProvider mSectionStyleProvider;
     private final NodeController mSilentNodeController;
     private final SectionHeaderController mSilentHeaderController;
     private final NodeController mAlertingHeaderController;
@@ -62,13 +62,13 @@
     public RankingCoordinator(
             StatusBarStateController statusBarStateController,
             HighPriorityProvider highPriorityProvider,
-            SectionClassifier sectionClassifier,
+            SectionStyleProvider sectionStyleProvider,
             @AlertingHeader NodeController alertingHeaderController,
             @SilentHeader SectionHeaderController silentHeaderController,
             @SilentHeader NodeController silentNodeController) {
         mStatusBarStateController = statusBarStateController;
         mHighPriorityProvider = highPriorityProvider;
-        mSectionClassifier = sectionClassifier;
+        mSectionStyleProvider = sectionStyleProvider;
         mAlertingHeaderController = alertingHeaderController;
         mSilentNodeController = silentNodeController;
         mSilentHeaderController = silentHeaderController;
@@ -77,7 +77,7 @@
     @Override
     public void attach(NotifPipeline pipeline) {
         mStatusBarStateController.addCallback(mStatusBarStateCallback);
-        mSectionClassifier.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner));
+        mSectionStyleProvider.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner));
 
         pipeline.addPreGroupFilter(mSuspendedFilter);
         pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index 4e9d3ac..9e8b35a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -19,11 +19,11 @@
 import android.content.Context
 import com.android.systemui.R
 import com.android.systemui.statusbar.notification.AssistantFeedbackController
-import com.android.systemui.statusbar.notification.SectionClassifier
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
 import com.android.systemui.statusbar.notification.collection.render.NotifRowController
 import javax.inject.Inject
 
@@ -35,7 +35,7 @@
 class RowAppearanceCoordinator @Inject internal constructor(
     context: Context,
     private var mAssistantFeedbackController: AssistantFeedbackController,
-    private var mSectionClassifier: SectionClassifier
+    private var mSectionStyleProvider: SectionStyleProvider
 ) : Coordinator {
 
     private var entryToExpand: NotificationEntry? = null
@@ -55,7 +55,7 @@
 
     private fun onBeforeRenderList(list: List<ListEntry>) {
         entryToExpand = list.firstOrNull()?.representativeEntry?.takeIf { entry ->
-            !mSectionClassifier.isMinimizedSection(entry.section!!)
+            !mSectionStyleProvider.isMinimizedSection(entry.section!!)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 497691d..2227be1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.statusbar.notification.collection.inflation
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.SectionClassifier
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
 import javax.inject.Inject
 
 /**
@@ -28,13 +28,13 @@
  */
 @SysUISingleton
 open class NotifUiAdjustmentProvider @Inject constructor(
-    private val sectionClassifier: SectionClassifier
+    private val sectionStyleProvider: SectionStyleProvider
 ) {
 
     private fun isEntryMinimized(entry: NotificationEntry): Boolean {
         val section = entry.section ?: error("Entry must have a section to determine if minimized")
         val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
-        val isMinimizedSection = sectionClassifier.isMinimizedSection(section)
+        val isMinimizedSection = sectionStyleProvider.isMinimizedSection(section)
         val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
         val isGroupSummary = parent.summary == entry
         return isMinimizedSection && (isTopLevelEntry || isGroupSummary)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
index 68bdd18..2148d3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification
+package com.android.systemui.statusbar.notification.collection.provider
 
 import android.content.Context
 import com.android.systemui.dagger.SysUISingleton
@@ -34,7 +34,7 @@
 class SectionHeaderVisibilityProvider @Inject constructor(
     context: Context
 ) {
-    var neverShowSectionHeaders = context.resources.getBoolean(R.bool.config_notification_never_show_section_headers)
-        private set
+    val neverShowSectionHeaders =
+        context.resources.getBoolean(R.bool.config_notification_never_show_section_headers)
     var sectionHeadersVisible = true
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
index 1f2d0fe..50e7d1ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification
+package com.android.systemui.statusbar.notification.collection.provider
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
@@ -26,7 +26,7 @@
  * NOTE: This class exists to avoid putting metadata like "isMinimized" on the NotifSection
  */
 @SysUISingleton
-class SectionClassifier @Inject constructor() {
+class SectionStyleProvider @Inject constructor() {
     private lateinit var lowPrioritySections: Set<NotifSectioner>
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index 6db544c..75a71af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -16,12 +16,12 @@
 
 package com.android.systemui.statusbar.notification.collection.render
 
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.util.Compile
 import com.android.systemui.util.traceSection
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 6ed8107..51dc728 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -19,10 +19,10 @@
 import android.content.Context
 import android.view.View
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.util.traceSection
 import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index d68f371..7fb115d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -52,9 +52,9 @@
     private var maxKeyguardNotifications by notNull<Int>()
 
     /**
-     * Minimum space between two notifications. There might be more space, see [calculateGapHeight].
+     * Minimum space between two notifications, see [calculateGapAndDividerHeight].
      */
-    private var notificationPadding by notNull<Int>()
+    private var dividerHeight by notNull<Int>()
 
     init {
         updateResources()
@@ -84,16 +84,20 @@
         val onLockscreen = true
         val showableRows = children.filter { it.isShowable(onLockscreen) }
         val showableRowsCount = showableRows.count()
+        log { "\tshowableRowsCount=$showableRowsCount "}
+
         showableRows.forEachIndexed { i, current ->
             val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen)
+            val spaceAfter = remainingSpace - spaceNeeded
             previous = current
-            log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace" }
+            log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace " +
+                    "spaceAfter=$spaceAfter" }
 
             if (remainingSpace - spaceNeeded >= 0 && count < maxKeyguardNotifications) {
                 count += 1
                 remainingSpace -= spaceNeeded
             } else if (remainingSpace - spaceNeeded > -shelfHeight && i == showableRowsCount - 1) {
-                log { "Showing all notifications. Shelf is not be needed." }
+                log { "Show all notifications. Shelf not needed." }
                 // If this is the last one, and it fits using the space shelf would use, then we can
                 // display it, as the shelf will not be needed (as all notifications are shown).
                 return count + 1
@@ -139,8 +143,7 @@
                 height += spaceNeeded
                 count += 1
             } else {
-                val gapBeforeFirstViewInShelf = current.calculateGapHeight(stack, previous, count)
-                height += gapBeforeFirstViewInShelf
+                height += current.calculateGapAndDividerHeight(stack, previous, count)
                 height += shelfHeight
                 log { "returning height with shelf -> $height" }
                 return height
@@ -155,7 +158,7 @@
         maxKeyguardNotifications =
             infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count))
 
-        notificationPadding =
+        dividerHeight =
             max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height))
     }
 
@@ -177,12 +180,7 @@
             } else {
                 intrinsicHeight.toFloat()
             }
-        if (visibleIndex != 0) {
-            size += notificationPadding
-        }
-        val gapHeight = calculateGapHeight(stack, previousView, visibleIndex)
-        log { "\ti=$visibleIndex gapHeight=$gapHeight"}
-        size += gapHeight
+        size += calculateGapAndDividerHeight(stack, previousView, visibleIndex)
         return size
     }
 
@@ -202,11 +200,17 @@
         return true
     }
 
-    private fun ExpandableView.calculateGapHeight(
+    private fun ExpandableView.calculateGapAndDividerHeight(
         stack: NotificationStackScrollLayout,
         previous: ExpandableView?,
         visibleIndex: Int
-    ) = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex)
+    ) : Float {
+        var height = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex)
+        if (visibleIndex != 0) {
+            height += dividerHeight
+        }
+        return height
+    }
 
     /**
      * Can a view be shown on the lockscreen when calculating the number of allowed notifications to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 415bd90..f3ee64f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Region;
+import android.os.Handler;
 import android.util.Pools;
 
 import androidx.collection.ArraySet;
@@ -29,6 +30,7 @@
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.StatusBarState;
@@ -104,8 +106,9 @@
             KeyguardBypassController bypassController,
             GroupMembershipManager groupMembershipManager,
             VisualStabilityProvider visualStabilityProvider,
-            ConfigurationController configurationController) {
-        super(context, logger);
+            ConfigurationController configurationController,
+            @Main Handler handler) {
+        super(context, logger, handler);
         Resources resources = mContext.getResources();
         mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
         statusBarStateController.addCallback(mStatusBarStateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 67de4e3..6b700a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -38,7 +38,6 @@
 import com.android.systemui.util.leak.RotationUtils.Rotation
 import com.android.systemui.util.leak.RotationUtils.getExactRotation
 import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
-
 import java.io.PrintWriter
 import java.lang.Math.max
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index bce5a15..e5d5ed4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.os.Handler;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.view.accessibility.AccessibilityManager;
@@ -34,6 +35,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -79,8 +81,9 @@
         }
     }
 
-    public HeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger) {
-        super(logger);
+    public HeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger,
+            @Main Handler handler) {
+        super(logger, handler);
         mContext = context;
         mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
         mUiEventLogger = Dependency.get(UiEventLogger.class);
@@ -94,7 +97,7 @@
 
         mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
                 SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
-        ContentObserver settingsObserver = new ContentObserver(mHandler) {
+        ContentObserver settingsObserver = new ContentObserver(handler) {
             @Override
             public void onChange(boolean selfChange) {
                 final int packageSnoozeLengthMs = Settings.Global.getInt(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 846e07f..5eff8cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -45,6 +45,7 @@
 import android.provider.Settings;
 import android.telephony.TelephonyCallback;
 import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -63,9 +64,8 @@
 import com.android.settingslib.users.UserCreatingDialog;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dumpable;
+import com.android.systemui.GuestResetOrExitSessionReceiver;
 import com.android.systemui.GuestResumeSessionReceiver;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
 import com.android.systemui.R;
 import com.android.systemui.SystemUISecondaryUserService;
 import com.android.systemui.animation.DialogLaunchAnimator;
@@ -84,6 +84,7 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.user.CreateUserActivity;
+import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.PrintWriter;
@@ -121,6 +122,8 @@
     private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
     @VisibleForTesting
     final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
+    @VisibleForTesting
+    final GuestResetOrExitSessionReceiver mGuestResetOrExitSessionReceiver;
     private final KeyguardStateController mKeyguardStateController;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final DevicePolicyManager mDevicePolicyManager;
@@ -144,6 +147,7 @@
     // When false, there won't be any visual affordance to add a new user from the keyguard even if
     // the user is unlocked
     private boolean mAddUsersFromLockScreen;
+    private boolean mUserSwitcherEnabled;
     @VisibleForTesting
     boolean mPauseRefreshUsers;
     private int mSecondaryUser = UserHandle.USER_NULL;
@@ -160,6 +164,7 @@
     private FalsingManager mFalsingManager;
     private View mView;
     private String mCreateSupervisedUserPackage;
+    private GlobalSettings mGlobalSettings;
 
     @Inject
     public UserSwitcherController(Context context,
@@ -177,13 +182,16 @@
             FalsingManager falsingManager,
             TelephonyListenerManager telephonyListenerManager,
             SecureSettings secureSettings,
+            GlobalSettings globalSettings,
             @Background Executor bgExecutor,
             @LongRunning Executor longRunningExecutor,
             @Main Executor uiExecutor,
             InteractionJankMonitor interactionJankMonitor,
             LatencyTracker latencyTracker,
             DumpManager dumpManager,
-            DialogLaunchAnimator dialogLaunchAnimator) {
+            DialogLaunchAnimator dialogLaunchAnimator,
+            GuestResumeSessionReceiver guestResumeSessionReceiver,
+            GuestResetOrExitSessionReceiver guestResetOrExitSessionReceiver) {
         mContext = context;
         mActivityManager = activityManager;
         mUserTracker = userTracker;
@@ -194,14 +202,14 @@
         mFalsingManager = falsingManager;
         mInteractionJankMonitor = interactionJankMonitor;
         mLatencyTracker = latencyTracker;
-        mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
-                this, mUserTracker, mUiEventLogger, secureSettings);
+        mGuestResumeSessionReceiver = guestResumeSessionReceiver;
+        mGuestResetOrExitSessionReceiver = guestResetOrExitSessionReceiver;
+        mGlobalSettings = globalSettings;
         mBgExecutor = bgExecutor;
         mLongRunningExecutor = longRunningExecutor;
         mUiExecutor = uiExecutor;
-        if (!UserManager.isGuestUserEphemeral()) {
-            mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
-        }
+        mGuestResumeSessionReceiver.register();
+        mGuestResetOrExitSessionReceiver.register();
         mGuestUserAutoCreated = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_guestUserAutoCreated);
         mGuestIsResetting = new AtomicBoolean();
@@ -237,8 +245,10 @@
             @Override
             public void onChange(boolean selfChange) {
                 mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
-                mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
+                mAddUsersFromLockScreen = mGlobalSettings.getIntForUser(
+                        Settings.Global.ADD_USERS_WHEN_LOCKED, 0, UserHandle.USER_SYSTEM) != 0;
+                mUserSwitcherEnabled = mGlobalSettings.getIntForUser(
+                        Settings.Global.USER_SWITCHER_ENABLED, 0, UserHandle.USER_SYSTEM) != 0;
                 refreshUsers(UserHandle.USER_NULL);
             };
         };
@@ -246,6 +256,9 @@
                 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
                 mSettingsObserver);
         mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED), true,
+                mSettingsObserver);
+        mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true,
                 mSettingsObserver);
         mContext.getContentResolver().registerContentObserver(
@@ -266,6 +279,10 @@
         refreshUsers(UserHandle.USER_NULL);
     }
 
+    private static boolean isEnableGuestModeUxChanges(Context context) {
+        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES);
+    }
+
     /**
      * Refreshes users from UserManager.
      *
@@ -314,6 +331,10 @@
             for (UserInfo info : infos) {
                 boolean isCurrent = currentId == info.id;
                 boolean switchToEnabled = canSwitchUsers || isCurrent;
+                if (!mUserSwitcherEnabled && !info.isPrimary()) {
+                    continue;
+                }
+
                 if (info.isEnabled()) {
                     if (info.isGuest()) {
                         // Tapping guest icon triggers remove and a user switch therefore
@@ -340,9 +361,6 @@
                     }
                 }
             }
-            if (records.size() > 1 || guestRecord != null) {
-                Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
-            }
 
             if (guestRecord == null) {
                 if (mGuestUserAutoCreated) {
@@ -411,12 +429,14 @@
     }
 
     boolean canCreateGuest(boolean hasExistingGuest) {
-        return (currentUserCanCreateUsers() || anyoneCanCreateUsers())
+        return mUserSwitcherEnabled
+                && (currentUserCanCreateUsers() || anyoneCanCreateUsers())
                 && !hasExistingGuest;
     }
 
     boolean canCreateUser() {
-        return (currentUserCanCreateUsers() || anyoneCanCreateUsers())
+        return mUserSwitcherEnabled
+                && (currentUserCanCreateUsers() || anyoneCanCreateUsers())
                 && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
     }
 
@@ -509,20 +529,31 @@
 
     private void onUserListItemClicked(int id, UserRecord record, DialogShower dialogShower) {
         int currUserId = mUserTracker.getUserId();
+        // If switching from guest and guest is ephemeral, then follow the flow
+        // of showExitGuestDialog to remove current guest,
+        // and switch to selected user
+        UserInfo currUserInfo = mUserTracker.getUserInfo();
         if (currUserId == id) {
             if (record.isGuest) {
-                showExitGuestDialog(id, dialogShower);
+                showExitGuestDialog(id, currUserInfo.isEphemeral(), dialogShower);
             }
             return;
         }
-        if (UserManager.isGuestUserEphemeral()) {
-            // If switching from guest, we want to bring up the guest exit dialog instead of switching
-            UserInfo currUserInfo = mUserManager.getUserInfo(currUserId);
-            if (currUserInfo != null && currUserInfo.isGuest()) {
-                showExitGuestDialog(currUserId, record.resolveId(), dialogShower);
+
+        if (currUserInfo != null && currUserInfo.isGuest()) {
+            if (isEnableGuestModeUxChanges(mContext)) {
+                showExitGuestDialog(currUserId, currUserInfo.isEphemeral(),
+                        record.resolveId(), dialogShower);
                 return;
+            } else {
+                if (currUserInfo.isEphemeral()) {
+                    showExitGuestDialog(currUserId, currUserInfo.isEphemeral(),
+                            record.resolveId(), dialogShower);
+                    return;
+                }
             }
         }
+
         if (dialogShower != null) {
             // If we haven't morphed into another dialog, it means we have just switched users.
             // Then, dismiss the dialog.
@@ -544,7 +575,7 @@
         }
     }
 
-    private void showExitGuestDialog(int id, DialogShower dialogShower) {
+    private void showExitGuestDialog(int id, boolean isGuestEphemeral, DialogShower dialogShower) {
         int newId = UserHandle.USER_SYSTEM;
         if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
             UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
@@ -552,14 +583,15 @@
                 newId = info.id;
             }
         }
-        showExitGuestDialog(id, newId, dialogShower);
+        showExitGuestDialog(id, isGuestEphemeral, newId, dialogShower);
     }
 
-    private void showExitGuestDialog(int id, int targetId, DialogShower dialogShower) {
+    private void showExitGuestDialog(int id, boolean isGuestEphemeral,
+                        int targetId, DialogShower dialogShower) {
         if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
             mExitGuestDialog.cancel();
         }
-        mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId);
+        mExitGuestDialog = new ExitGuestDialog(mContext, id, isGuestEphemeral, targetId);
         if (dialogShower != null) {
             dialogShower.showDialog(mExitGuestDialog);
         } else {
@@ -792,6 +824,52 @@
         }
     }
 
+    /**
+     * Exits guest user and switches to previous non-guest user. The guest must be the current
+     * user.
+     *
+     * @param guestUserId user id of the guest user to exit
+     * @param targetUserId user id of the guest user to exit, set to UserHandle.USER_NULL when
+     *                       target user id is not known
+     * @param forceRemoveGuestOnExit true: remove guest before switching user,
+     *                               false: remove guest only if its ephemeral, else keep guest
+     */
+    public void exitGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId,
+                    boolean forceRemoveGuestOnExit) {
+        UserInfo currentUser = mUserTracker.getUserInfo();
+        if (currentUser.id != guestUserId) {
+            Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
+                    + " is not current user (" + currentUser.id + ")");
+            return;
+        }
+        if (!currentUser.isGuest()) {
+            Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
+                    + " is not a guest");
+            return;
+        }
+
+        int newUserId = UserHandle.USER_SYSTEM;
+        if (targetUserId == UserHandle.USER_NULL) {
+            // when target user is not specified switch to last non guest user
+            if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
+                UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
+                if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
+                    newUserId = info.id;
+                }
+            }
+        } else {
+            newUserId = targetUserId;
+        }
+
+        if (currentUser.isEphemeral() || forceRemoveGuestOnExit) {
+            mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
+            removeGuestUser(currentUser.id, newUserId);
+        } else {
+            mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_SWITCH);
+            switchToUserId(newUserId);
+        }
+    }
+
     private void scheduleGuestCreation() {
         if (!mGuestCreationScheduled.compareAndSet(false, true)) {
             return;
@@ -959,9 +1037,14 @@
         public String getName(Context context, UserRecord item) {
             if (item.isGuest) {
                 if (item.isCurrent) {
-                    return context.getString(mController.mGuestUserAutoCreated
+                    if (isEnableGuestModeUxChanges(context)) {
+                        return context.getString(
+                                com.android.settingslib.R.string.guest_exit_quick_settings_button);
+                    } else {
+                        return context.getString(mController.mGuestUserAutoCreated
                             ? com.android.settingslib.R.string.guest_reset_guest
                             : com.android.settingslib.R.string.guest_exit_guest);
+                    }
                 } else {
                     if (item.info != null) {
                         return context.getString(com.android.internal.R.string.guest_name);
@@ -978,8 +1061,13 @@
                                             ? com.android.settingslib.R.string.guest_resetting
                                             : com.android.internal.R.string.guest_name);
                         } else {
-                            return context.getString(
-                                    com.android.settingslib.R.string.guest_new_guest);
+                            if (isEnableGuestModeUxChanges(context)) {
+                                // we always show "guest" as string, instead of "add guest"
+                                return context.getString(com.android.internal.R.string.guest_name);
+                            } else {
+                                return context.getString(
+                                        com.android.settingslib.R.string.guest_new_guest);
+                            }
                         }
                     }
                 }
@@ -1001,7 +1089,11 @@
         protected static Drawable getIconDrawable(Context context, UserRecord item) {
             int iconRes;
             if (item.isAddUser) {
-                iconRes = R.drawable.ic_account_circle_filled;
+                if (isEnableGuestModeUxChanges(context)) {
+                    iconRes = R.drawable.ic_add;
+                } else {
+                    iconRes = R.drawable.ic_account_circle_filled;
+                }
             } else if (item.isGuest) {
                 iconRes = R.drawable.ic_account_circle;
             } else if (item.isAddSupervisedUser) {
@@ -1034,8 +1126,8 @@
     private boolean shouldUseSimpleUserSwitcher() {
         int defaultSimpleUserSwitcher = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_expandLockScreenUserSwitcher) ? 1 : 0;
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                SIMPLE_USER_SWITCHER_GLOBAL_SETTING, defaultSimpleUserSwitcher) != 0;
+        return mGlobalSettings.getIntForUser(SIMPLE_USER_SWITCHER_GLOBAL_SETTING,
+                defaultSimpleUserSwitcher, UserHandle.USER_SYSTEM) != 0;
     }
 
     public void startActivity(Intent intent) {
@@ -1146,24 +1238,58 @@
 
         private final int mGuestId;
         private final int mTargetId;
+        private final boolean mIsGuestEphemeral;
 
-        public ExitGuestDialog(Context context, int guestId, int targetId) {
+        ExitGuestDialog(Context context, int guestId, boolean isGuestEphemeral,
+                    int targetId) {
             super(context);
-            setTitle(mGuestUserAutoCreated
-                    ? com.android.settingslib.R.string.guest_reset_guest_dialog_title
-                    : com.android.settingslib.R.string.guest_remove_guest_dialog_title);
-            setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
-            setButton(DialogInterface.BUTTON_NEUTRAL,
-                    context.getString(android.R.string.cancel), this);
-            setButton(DialogInterface.BUTTON_POSITIVE,
-                    context.getString(mGuestUserAutoCreated
+            if (isEnableGuestModeUxChanges(context)) {
+                if (isGuestEphemeral) {
+                    setTitle(context.getString(
+                                com.android.settingslib.R.string.guest_exit_dialog_title));
+                    setMessage(context.getString(
+                                com.android.settingslib.R.string.guest_exit_dialog_message));
+                    setButton(DialogInterface.BUTTON_NEUTRAL,
+                            context.getString(android.R.string.cancel), this);
+                    setButton(DialogInterface.BUTTON_POSITIVE,
+                            context.getString(
+                                com.android.settingslib.R.string.guest_exit_dialog_button), this);
+                } else {
+                    setTitle(context.getString(
+                                com.android.settingslib
+                                    .R.string.guest_exit_dialog_title_non_ephemeral));
+                    setMessage(context.getString(
+                                com.android.settingslib
+                                    .R.string.guest_exit_dialog_message_non_ephemeral));
+                    setButton(DialogInterface.BUTTON_NEUTRAL,
+                            context.getString(android.R.string.cancel), this);
+                    setButton(DialogInterface.BUTTON_NEGATIVE,
+                            context.getString(
+                                com.android.settingslib.R.string.guest_exit_clear_data_button),
+                            this);
+                    setButton(DialogInterface.BUTTON_POSITIVE,
+                            context.getString(
+                                com.android.settingslib.R.string.guest_exit_save_data_button),
+                            this);
+                }
+            } else {
+                setTitle(mGuestUserAutoCreated
+                        ? com.android.settingslib.R.string.guest_reset_guest_dialog_title
+                        : com.android.settingslib.R.string.guest_remove_guest_dialog_title);
+                setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
+                setButton(DialogInterface.BUTTON_NEUTRAL,
+                        context.getString(android.R.string.cancel), this);
+                setButton(DialogInterface.BUTTON_POSITIVE,
+                        context.getString(mGuestUserAutoCreated
                             ? com.android.settingslib.R.string.guest_reset_guest_confirm_button
                             : com.android.settingslib.R.string.guest_remove_guest_confirm_button),
-                    this);
+                        this);
+            }
             SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing());
             setCanceledOnTouchOutside(false);
             mGuestId = guestId;
             mTargetId = targetId;
+            mIsGuestEphemeral = isGuestEphemeral;
         }
 
         @Override
@@ -1173,12 +1299,40 @@
             if (mFalsingManager.isFalseTap(penalty)) {
                 return;
             }
-            if (which == BUTTON_NEUTRAL) {
-                cancel();
+            if (isEnableGuestModeUxChanges(getContext())) {
+                if (mIsGuestEphemeral) {
+                    if (which == DialogInterface.BUTTON_POSITIVE) {
+                        mDialogLaunchAnimator.dismissStack(this);
+                        // Ephemeral guest: exit guest, guest is removed by the system
+                        // on exit, since its marked ephemeral
+                        exitGuestUser(mGuestId, mTargetId, false);
+                    } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+                        // Cancel clicked, do nothing
+                        cancel();
+                    }
+                } else {
+                    if (which == DialogInterface.BUTTON_POSITIVE) {
+                        mDialogLaunchAnimator.dismissStack(this);
+                        // Non-ephemeral guest: exit guest, guest is not removed by the system
+                        // on exit, since its marked non-ephemeral
+                        exitGuestUser(mGuestId, mTargetId, false);
+                    } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+                        mDialogLaunchAnimator.dismissStack(this);
+                        // Non-ephemeral guest: remove guest and then exit
+                        exitGuestUser(mGuestId, mTargetId, true);
+                    } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+                        // Cancel clicked, do nothing
+                        cancel();
+                    }
+                }
             } else {
-                mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
-                mDialogLaunchAnimator.dismissStack(this);
-                removeGuestUser(mGuestId, mTargetId);
+                if (which == BUTTON_NEUTRAL) {
+                    cancel();
+                } else {
+                    mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
+                    mDialogLaunchAnimator.dismissStack(this);
+                    removeGuestUser(mGuestId, mTargetId);
+                }
             }
         }
     }
@@ -1187,10 +1341,17 @@
     final class AddUserDialog extends SystemUIDialog implements
             DialogInterface.OnClickListener {
 
-        public AddUserDialog(Context context) {
+        AddUserDialog(Context context) {
             super(context);
+
             setTitle(com.android.settingslib.R.string.user_add_user_title);
-            setMessage(com.android.settingslib.R.string.user_add_user_message_short);
+            String message = context.getString(
+                                com.android.settingslib.R.string.user_add_user_message_short);
+            UserInfo currentUser = mUserTracker.getUserInfo();
+            if (currentUser != null && currentUser.isGuest() && currentUser.isEphemeral()) {
+                message += context.getString(R.string.user_add_user_message_guest_remove);
+            }
+            setMessage(message);
             setButton(DialogInterface.BUTTON_NEUTRAL,
                     context.getString(android.R.string.cancel), this);
             setButton(DialogInterface.BUTTON_POSITIVE,
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index b7f90a4..c92491e 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -169,7 +169,8 @@
             KeyguardBypassController bypassController,
             GroupMembershipManager groupManager,
             VisualStabilityProvider visualStabilityProvider,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            @Main Handler handler) {
         return new HeadsUpManagerPhone(
                 context,
                 headsUpManagerLogger,
@@ -177,7 +178,8 @@
                 bypassController,
                 groupManager,
                 visualStabilityProvider,
-                configurationController
+                configurationController,
+                handler
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
index e42d537..603cf3b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap
 import android.graphics.Canvas
+import android.graphics.Color
 import android.graphics.Typeface
 import android.graphics.fonts.Font
 import android.graphics.fonts.FontFamily
@@ -194,6 +195,128 @@
 
         assertThat(expected.sameAs(actual)).isTrue()
     }
+
+    @Test
+    fun testGlyphCallback_Empty() {
+        val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
+
+        val interp = TextInterpolator(layout).apply {
+            glyphFilter = { glyph, progress ->
+            }
+        }
+        interp.basePaint.set(START_PAINT)
+        interp.onBasePaintModified()
+
+        interp.targetPaint.set(END_PAINT)
+        interp.onTargetPaintModified()
+
+        // Just after created TextInterpolator, it should have 0 progress.
+        val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+                .toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+        assertThat(expected.sameAs(actual)).isTrue()
+    }
+
+    @Test
+    fun testGlyphCallback_Xcoordinate() {
+        val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
+
+        val interp = TextInterpolator(layout).apply {
+            glyphFilter = { glyph, progress ->
+                glyph.x += 30f
+            }
+        }
+        interp.basePaint.set(START_PAINT)
+        interp.onBasePaintModified()
+
+        interp.targetPaint.set(END_PAINT)
+        interp.onTargetPaintModified()
+
+        // Just after created TextInterpolator, it should have 0 progress.
+        val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+                .toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+        // The glyph position was modified by callback, so the bitmap should not be the same.
+        // We cannot modify the result of StaticLayout, so we cannot expect the exact  bitmaps.
+        assertThat(expected.sameAs(actual)).isFalse()
+    }
+
+    @Test
+    fun testGlyphCallback_Ycoordinate() {
+        val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
+
+        val interp = TextInterpolator(layout).apply {
+            glyphFilter = { glyph, progress ->
+                glyph.y += 30f
+            }
+        }
+        interp.basePaint.set(START_PAINT)
+        interp.onBasePaintModified()
+
+        interp.targetPaint.set(END_PAINT)
+        interp.onTargetPaintModified()
+
+        // Just after created TextInterpolator, it should have 0 progress.
+        val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+                .toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+        // The glyph position was modified by callback, so the bitmap should not be the same.
+        // We cannot modify the result of StaticLayout, so we cannot expect the exact  bitmaps.
+        assertThat(expected.sameAs(actual)).isFalse()
+    }
+
+    @Test
+    fun testGlyphCallback_TextSize() {
+        val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
+
+        val interp = TextInterpolator(layout).apply {
+            glyphFilter = { glyph, progress ->
+                glyph.textSize += 10f
+            }
+        }
+        interp.basePaint.set(START_PAINT)
+        interp.onBasePaintModified()
+
+        interp.targetPaint.set(END_PAINT)
+        interp.onTargetPaintModified()
+
+        // Just after created TextInterpolator, it should have 0 progress.
+        val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+                .toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+        // The glyph position was modified by callback, so the bitmap should not be the same.
+        // We cannot modify the result of StaticLayout, so we cannot expect the exact  bitmaps.
+        assertThat(expected.sameAs(actual)).isFalse()
+    }
+
+    @Test
+    fun testGlyphCallback_Color() {
+        val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
+
+        val interp = TextInterpolator(layout).apply {
+            glyphFilter = { glyph, progress ->
+                glyph.color = Color.RED
+            }
+        }
+        interp.basePaint.set(START_PAINT)
+        interp.onBasePaintModified()
+
+        interp.targetPaint.set(END_PAINT)
+        interp.onTargetPaintModified()
+
+        // Just after created TextInterpolator, it should have 0 progress.
+        val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+                .toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+        // The glyph position was modified by callback, so the bitmap should not be the same.
+        // We cannot modify the result of StaticLayout, so we cannot expect the exact  bitmaps.
+        assertThat(expected.sameAs(actual)).isFalse()
+    }
 }
 
 private fun Layout.toBitmap(width: Int, height: Int) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 1921cb6..68eebed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.media
 
+import android.app.Notification
 import android.app.Notification.MediaStyle
 import android.app.PendingIntent
 import android.app.smartspace.SmartspaceAction
@@ -621,6 +622,36 @@
     }
 
     @Test
+    fun testTooManyNotificationActions_isTruncated() {
+        // GIVEN a notification where too many notification actions are added
+        val action = Notification.Action(R.drawable.ic_android, "action", null)
+        val notif = SbnBuilder().run {
+            setPkg(PACKAGE_NAME)
+            modifyNotification(context).also {
+                it.setSmallIcon(android.R.drawable.ic_media_pause)
+                it.setStyle(MediaStyle().apply {
+                    setMediaSession(session.sessionToken)
+                })
+                for (i in 0..MediaDataManager.MAX_NOTIFICATION_ACTIONS) {
+                    it.addAction(action)
+                }
+            }
+            build()
+        }
+
+        // WHEN the notification is loaded
+        mediaDataManager.onNotificationAdded(KEY, notif)
+        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+
+        // THEN only the first MAX_NOTIFICATION_ACTIONS are actually included
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+            eq(0), eq(false))
+        assertThat(mediaDataCaptor.value.actions.size).isEqualTo(
+            MediaDataManager.MAX_NOTIFICATION_ACTIONS)
+    }
+
+    @Test
     fun testPlaybackActions_noState_usesNotification() {
         val desc = "Notification Action"
         whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
@@ -781,6 +812,25 @@
     }
 
     @Test
+    fun testPlaybackActions_playPause_hasButton() {
+        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        val stateActions = PlaybackState.ACTION_PLAY_PAUSE
+        val stateBuilder = PlaybackState.Builder().setActions(stateActions)
+        whenever(controller.playbackState).thenReturn(stateBuilder.build())
+
+        addNotificationAndLoad()
+
+        assertThat(mediaDataCaptor.value!!.semanticActions).isNotNull()
+        val actions = mediaDataCaptor.value!!.semanticActions!!
+
+        assertThat(actions.playOrPause).isNotNull()
+        assertThat(actions.playOrPause!!.contentDescription).isEqualTo(
+            context.getString(R.string.controls_media_button_play))
+        actions.playOrPause!!.action!!.run()
+        verify(transportControls).play()
+    }
+
+    @Test
     fun testPlaybackLocationChange_isLogged() {
         // Media control added for local playback
         addNotificationAndLoad()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 067607f..9edc4f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -102,7 +102,7 @@
             TapGestureDetector(context),
             powerManager,
             Handler.getMain(),
-            receiverUiEventLogger,
+            receiverUiEventLogger
         )
 
         val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
@@ -206,6 +206,18 @@
         assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize)
     }
 
+    @Test
+    fun commandQueueCallback_invalidStateParam_noChipShown() {
+        commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+            routeInfo,
+            null,
+            APP_NAME
+        )
+
+        verify(windowManager, never()).addView(any(), any())
+    }
+
     private fun getChipView(): ViewGroup {
         val viewCaptor = ArgumentCaptor.forClass(View::class.java)
         verify(windowManager).addView(viewCaptor.capture(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 8a38847..65d0adc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -83,12 +83,10 @@
     private final class TestableAlertingNotificationManager extends AlertingNotificationManager {
         private AlertEntry mLastCreatedEntry;
 
-        private TestableAlertingNotificationManager() {
-            super(mock(HeadsUpManagerLogger.class));
+        private TestableAlertingNotificationManager(Handler handler) {
+            super(mock(HeadsUpManagerLogger.class), handler);
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
-            mHandler.removeCallbacksAndMessages(null);
-            mHandler = mTestHandler;
         }
 
         @Override
@@ -109,8 +107,8 @@
         }
     }
 
-    protected AlertingNotificationManager createAlertingNotificationManager() {
-        return new TestableAlertingNotificationManager();
+    protected AlertingNotificationManager createAlertingNotificationManager(Handler handler) {
+        return new TestableAlertingNotificationManager(handler);
     }
 
     protected StatusBarNotification createNewSbn(int id, Notification.Builder n) {
@@ -144,7 +142,7 @@
                 .build();
         mEntry.setRow(mRow);
 
-        mAlertingNotificationManager = createAlertingNotificationManager();
+        mAlertingNotificationManager = createAlertingNotificationManager(mTestHandler);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index a2a02cd..30c01b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -816,8 +816,11 @@
         mExecutor.runAllReady();
         reset(mRotateTextViewController);
 
-        // GIVEN keyguard is showing
+        // GIVEN keyguard is showing and not dozing
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mController.setVisible(true);
+        mExecutor.runAllReady();
+        reset(mRotateTextViewController);
 
         // WHEN keyguard showing changed called
         mKeyguardStateControllerCallback.onKeyguardShowingChanged();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index 52bacd2..d8a98d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -29,12 +29,12 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 3b034f7..fc74f39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -41,7 +41,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.notification.SectionClassifier;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -56,6 +55,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
 import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn;
 import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
 
@@ -96,9 +96,9 @@
     @Mock private IStatusBarService mService;
     @Mock private BindEventManagerImpl mBindEventManagerImpl;
     @Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
-    private final SectionClassifier mSectionClassifier = new SectionClassifier();
+    private final SectionStyleProvider mSectionStyleProvider = new SectionStyleProvider();
     private final NotifUiAdjustmentProvider mAdjustmentProvider =
-            new NotifUiAdjustmentProvider(mSectionClassifier);
+            new NotifUiAdjustmentProvider(mSectionStyleProvider);
 
     @NonNull
     private NotificationEntryBuilder getNotificationEntryBuilder() {
@@ -487,7 +487,7 @@
     private static final int TEST_MAX_GROUP_DELAY = 100;
 
     private void setSectionIsLowPriority(boolean minimized) {
-        mSectionClassifier.setMinimizedSections(minimized
+        mSectionStyleProvider.setMinimizedSections(minimized
                 ? Collections.singleton(mNotifSection.getSectioner())
                 : Collections.emptyList());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index f4d8405..ff08bc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -40,7 +40,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
-import com.android.systemui.statusbar.notification.SectionClassifier;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -48,6 +47,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
 import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 
@@ -68,7 +68,7 @@
 
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private HighPriorityProvider mHighPriorityProvider;
-    @Mock private SectionClassifier mSectionClassifier;
+    @Mock private SectionStyleProvider mSectionStyleProvider;
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private NodeController mAlertingHeaderController;
     @Mock private NodeController mSilentNodeController;
@@ -92,7 +92,7 @@
         mRankingCoordinator = new RankingCoordinator(
                 mStatusBarStateController,
                 mHighPriorityProvider,
-                mSectionClassifier,
+                mSectionStyleProvider,
                 mAlertingHeaderController,
                 mSilentHeaderController,
                 mSilentNodeController);
@@ -100,7 +100,7 @@
         mEntry.setRanking(getRankingForUnfilteredNotif().build());
 
         mRankingCoordinator.attach(mNotifPipeline);
-        verify(mSectionClassifier).setMinimizedSections(any());
+        verify(mSectionStyleProvider).setMinimizedSections(any());
         verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
         mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
         mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
index 447ba15..40859d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
@@ -21,13 +21,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.notification.AssistantFeedbackController
 import com.android.systemui.statusbar.notification.FeedbackIcon
-import com.android.systemui.statusbar.notification.SectionClassifier
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
 import com.android.systemui.statusbar.notification.collection.render.NotifRowController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
@@ -53,7 +53,7 @@
 
     @Mock private lateinit var pipeline: NotifPipeline
     @Mock private lateinit var assistantFeedbackController: AssistantFeedbackController
-    @Mock private lateinit var sectionClassifier: SectionClassifier
+    @Mock private lateinit var sectionStyleProvider: SectionStyleProvider
 
     @Mock private lateinit var section1: NotifSection
     @Mock private lateinit var section2: NotifSection
@@ -66,7 +66,7 @@
         coordinator = RowAppearanceCoordinator(
             mContext,
             assistantFeedbackController,
-            sectionClassifier
+            sectionStyleProvider
         )
         coordinator.attach(pipeline)
         beforeRenderListListener = withArgCaptor {
@@ -82,8 +82,8 @@
 
     @Test
     fun testSetSystemExpandedOnlyOnFirst() {
-        whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(false)
-        whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(false)
+        whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(false)
+        whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(false)
         beforeRenderListListener.onBeforeRenderList(listOf(entry1, entry2))
         afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
         verify(controller1).setSystemExpanded(eq(true))
@@ -93,8 +93,8 @@
 
     @Test
     fun testSetSystemExpandedNeverIfMinimized() {
-        whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(true)
-        whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(true)
+        whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(true)
+        whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(true)
         beforeRenderListListener.onBeforeRenderList(listOf(entry1, entry2))
         afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
         verify(controller1).setSystemExpanded(eq(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index 0e18658..ff60193 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -19,7 +19,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -28,6 +27,7 @@
 import com.android.systemui.statusbar.notification.collection.getAttachState
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1ecb09b..3d57f66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -144,10 +144,9 @@
                 mock(KeyguardBypassController.class),
                 mock(NotificationGroupManagerLegacy.class),
                 mock(VisualStabilityProvider.class),
-                mock(ConfigurationControllerImpl.class)
+                mock(ConfigurationControllerImpl.class),
+                new Handler(mTestLooper.getLooper())
         );
-        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
-        mHeadsUpManager.mHandler = new Handler(mTestLooper.getLooper());
         mGroupMembershipManager.setHeadsUpManager(mHeadsUpManager);
         mIconManager = new IconManager(
                 mock(CommonNotifCollection.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 497a857..dfd70a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -57,7 +57,7 @@
             .thenReturn(GAP_HEIGHT)
         with(testableResources) {
             addOverride(R.integer.keyguard_max_notification_count, -1)
-            addOverride(R.dimen.notification_divider_height, NOTIFICATION_PADDING.toInt())
+            addOverride(R.dimen.notification_divider_height, DIVIDER_HEIGHT.toInt())
         }
 
         sizeCalculator =
@@ -109,7 +109,7 @@
     fun computeMaxKeyguardNotifications_spaceForOne_shelfUsableForLastNotification_returnsTwo() {
         val rowHeight = ROW_HEIGHT
         val totalSpaceForEachRow = GAP_HEIGHT + rowHeight
-        val shelfHeight = totalSpaceForEachRow + NOTIFICATION_PADDING
+        val shelfHeight = totalSpaceForEachRow + DIVIDER_HEIGHT
         val spaceForOne = totalSpaceForEachRow
         val rows =
             listOf(
@@ -127,7 +127,7 @@
     fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() {
         val rowHeight = ROW_HEIGHT
         val totalSpaceForEachRow = GAP_HEIGHT + rowHeight
-        val spaceForTwo = totalSpaceForEachRow * 2 + NOTIFICATION_PADDING
+        val spaceForTwo = totalSpaceForEachRow * 2 + DIVIDER_HEIGHT
         val rows =
             listOf(
                 createMockRow(rowHeight),
@@ -143,7 +143,7 @@
     fun computeHeight_returnsAtMostSpaceAvailable_withGapBeforeShelf() {
         val rowHeight = ROW_HEIGHT
         val shelfHeight = SHELF_HEIGHT
-        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + NOTIFICATION_PADDING
+        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT
         val availableSpace = totalSpaceForEachRow * 2
 
         // All rows in separate sections (default setup).
@@ -164,7 +164,7 @@
     fun computeHeight_returnsAtMostSpaceAvailable_noGapBeforeShelf() {
         val rowHeight = ROW_HEIGHT
         val shelfHeight = SHELF_HEIGHT
-        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + NOTIFICATION_PADDING
+        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT
         val availableSpace = totalSpaceForEachRow * 1
 
         // Both rows are in the same section.
@@ -223,7 +223,7 @@
     /** Default dimensions for tests that don't overwrite them. */
     companion object {
         const val GAP_HEIGHT = 12f
-        const val NOTIFICATION_PADDING = 3f
+        const val DIVIDER_HEIGHT = 3f
         const val SHELF_HEIGHT = 14f
         const val ROW_HEIGHT = SHELF_HEIGHT * 3
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index db5741c..b8c8b5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
@@ -76,7 +77,8 @@
                 VisualStabilityProvider visualStabilityProvider,
                 StatusBarStateController statusBarStateController,
                 KeyguardBypassController keyguardBypassController,
-                ConfigurationController configurationController
+                ConfigurationController configurationController,
+                Handler handler
         ) {
             super(
                     context,
@@ -85,7 +87,8 @@
                     keyguardBypassController,
                     groupManager,
                     visualStabilityProvider,
-                    configurationController
+                    configurationController,
+                    handler
             );
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -105,6 +108,8 @@
         when(mVSProvider.isReorderingAllowed()).thenReturn(true);
         mDependency.injectMockDependency(NotificationShadeWindowController.class);
         mDependency.injectMockDependency(ConfigurationController.class);
+        super.setUp();
+
         mHeadsUpManager = new TestableHeadsUpManagerPhone(
                 mContext,
                 mHeadsUpManagerLogger,
@@ -112,11 +117,9 @@
                 mVSProvider,
                 mStatusBarStateController,
                 mBypassController,
-                mConfigurationController
+                mConfigurationController,
+                mTestHandler
         );
-        super.setUp();
-        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
-        mHeadsUpManager.mHandler = mTestHandler;
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 7070bc1..56dfb0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
+import android.os.Handler;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -51,7 +52,6 @@
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 import com.android.wm.shell.bubbles.Bubbles;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -89,7 +89,8 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mHeadsUpManager = new HeadsUpManager(mContext, mock(HeadsUpManagerLogger.class)) {};
+        mHeadsUpManager = new HeadsUpManager(mContext, mock(HeadsUpManagerLogger.class),
+                mock(Handler.class)) {};
 
         when(mNotificationEntryManager.getPendingNotificationsIterator())
                 .thenReturn(mPendingEntries.values());
@@ -114,11 +115,6 @@
         mHeadsUpManager.addListener(mGroupAlertTransferHelper);
     }
 
-    @After
-    public void tearDown() {
-        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
-    }
-
     private void mockHasHeadsUpContentView(NotificationEntry entry,
             boolean hasHeadsUpContentView) {
         RowContentBindParams params = new RowContentBindParams();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 424a40058..f39d687 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -36,6 +36,7 @@
 import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -73,8 +74,8 @@
     @Mock private HeadsUpManagerLogger mLogger;
 
     private final class TestableHeadsUpManager extends HeadsUpManager {
-        TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger) {
-            super(context, logger);
+        TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger, Handler handler) {
+            super(context, logger, handler);
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
         }
@@ -91,10 +92,9 @@
         mDependency.injectTestDependency(UiEventLogger.class, mUiEventLoggerFake);
         when(mEntry.getSbn()).thenReturn(mSbn);
         when(mSbn.getNotification()).thenReturn(mNotification);
-        mHeadsUpManager = new TestableHeadsUpManager(mContext, mLogger);
+
         super.setUp();
-        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
-        mHeadsUpManager.mHandler = mTestHandler;
+        mHeadsUpManager = new TestableHeadsUpManager(mContext, mLogger, mTestHandler);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index 799dafc..152815f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.policy
 
 import android.app.IActivityManager
+import android.app.NotificationManager
 import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.DialogInterface
@@ -28,6 +29,7 @@
 import android.os.Handler
 import android.os.UserHandle
 import android.os.UserManager
+import android.provider.Settings
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.ThreadedRenderer
@@ -36,7 +38,9 @@
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.util.LatencyTracker
 import com.android.internal.util.UserIcons
+import com.android.systemui.GuestResetOrExitSessionReceiver
 import com.android.systemui.GuestResumeSessionReceiver
+import com.android.systemui.GuestSessionNotification
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
@@ -51,6 +55,7 @@
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView
 import com.android.systemui.telephony.TelephonyListenerManager
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.settings.GlobalSettings
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Assert.assertEquals
@@ -67,6 +72,7 @@
 import org.mockito.Mockito.any
 import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -95,6 +101,12 @@
     @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
     @Mock private lateinit var threadedRenderer: ThreadedRenderer
     @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+    @Mock private lateinit var globalSettings: GlobalSettings
+    @Mock private lateinit var guestSessionNotification: GuestSessionNotification
+    @Mock private lateinit var guestResetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
+    private lateinit var resetSessionDialogFactory:
+                            GuestResumeSessionReceiver.ResetSessionDialog.Factory
+    private lateinit var guestResumeSessionReceiver: GuestResumeSessionReceiver
     private lateinit var testableLooper: TestableLooper
     private lateinit var bgExecutor: FakeExecutor
     private lateinit var longRunningExecutor: FakeExecutor
@@ -126,9 +138,28 @@
                 com.android.internal.R.bool.config_guestUserAutoCreated, false)
 
         mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
+        mContext.addMockSystemService(Context.NOTIFICATION_SERVICE,
+                mock(NotificationManager::class.java))
         mContext.addMockSystemService(Context.FINGERPRINT_SERVICE,
                 mock(FingerprintManager::class.java))
 
+        resetSessionDialogFactory = object : GuestResumeSessionReceiver.ResetSessionDialog.Factory {
+                override fun create(userId: Int): GuestResumeSessionReceiver.ResetSessionDialog {
+                    return GuestResumeSessionReceiver.ResetSessionDialog(
+                                mContext,
+                                mock(UserSwitcherController::class.java),
+                                uiEventLogger,
+                                userId
+                            )
+                }
+            }
+
+        guestResumeSessionReceiver = GuestResumeSessionReceiver(userTracker,
+                                        secureSettings,
+                                        broadcastDispatcher,
+                                        guestSessionNotification,
+                                        resetSessionDialogFactory)
+
         `when`(userManager.canAddMoreUsers(eq(UserManager.USER_TYPE_FULL_SECONDARY)))
                 .thenReturn(true)
         `when`(notificationShadeWindowView.context).thenReturn(context)
@@ -148,6 +179,22 @@
         `when`(userTracker.userId).thenReturn(ownerId)
         `when`(userTracker.userInfo).thenReturn(ownerInfo)
 
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.ADD_USERS_WHEN_LOCKED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(1)
+
         setupController()
     }
 
@@ -168,13 +215,16 @@
                 falsingManager,
                 telephonyListenerManager,
                 secureSettings,
+                globalSettings,
                 bgExecutor,
                 longRunningExecutor,
                 uiExecutor,
                 interactionJankMonitor,
                 latencyTracker,
                 dumpManager,
-                dialogLaunchAnimator)
+                dialogLaunchAnimator,
+                guestResumeSessionReceiver,
+                guestResetOrExitSessionReceiver)
         userSwitcherController.init(notificationShadeWindowView)
     }
 
@@ -264,7 +314,10 @@
                 .getButton(DialogInterface.BUTTON_POSITIVE).performClick()
         testableLooper.processAllMessages()
         assertEquals(1, uiEventLogger.numLogs())
-        assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0))
+        assertTrue(
+            QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id == uiEventLogger.eventId(0) ||
+            QSUserSwitcherEvent.QS_USER_SWITCH.id == uiEventLogger.eventId(0)
+        )
     }
 
     @Test
@@ -326,7 +379,7 @@
         userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
         assertNotNull(userSwitcherController.mExitGuestDialog)
         userSwitcherController.mExitGuestDialog
-                .getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
+                .getButton(DialogInterface.BUTTON_NEUTRAL).performClick()
         testableLooper.processAllMessages()
         assertEquals(0, uiEventLogger.numLogs())
     }
@@ -469,4 +522,43 @@
         // THEN a supervised user can NOT be constructed
         assertFalse(userSwitcherController.canCreateSupervisedUser())
     }
+
+    @Test
+    fun testCannotCreateUserWhenUserSwitcherDisabled() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+        setupController()
+        assertFalse(userSwitcherController.canCreateUser())
+    }
+
+    @Test
+    fun testCannotCreateGuestUserWhenUserSwitcherDisabled() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+        setupController()
+        assertFalse(userSwitcherController.canCreateGuest(false))
+    }
+
+    @Test
+    fun testCannotCreateSupervisedUserWhenUserSwitcherDisabled() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+        setupController()
+        assertFalse(userSwitcherController.canCreateSupervisedUser())
+    }
 }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index dfa34bb..4ca83dd 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -286,6 +286,11 @@
     // Package: android
     NOTE_MTE_OVERRIDE_ENABLED = 69;
 
+    // Notify the user that this is a guest session with information
+    // about first login and ephemeral state
+    // Package: android
+    NOTE_GUEST_SESSION = 70;
+
     // Inform the user of notification permissions changes.
     // Package: android
     NOTE_REVIEW_NOTIFICATION_PERMISSIONS = 71;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 6846b2e..03de43c 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -45,6 +45,7 @@
 import android.graphics.Region;
 import android.os.Handler;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 import android.view.InputDevice;
@@ -84,12 +85,14 @@
 public class TouchExplorer extends BaseEventStreamTransformation
         implements GestureManifold.Listener {
 
-    static final boolean DEBUG = false;
     private static final long LOGGING_FLAGS = FLAGS_GESTURE | FLAGS_INPUT_FILTER;
 
     // Tag for logging received events.
     private static final String LOG_TAG = "TouchExplorer";
 
+    // To enable these logs, run: 'adb shell setprop log.tag.TouchExplorer DEBUG' (requires restart)
+    static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
+
     // The maximum of the cosine between the vectors of two moving
     // pointers so they can be considered moving in the same direction.
     private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
new file mode 100644
index 0000000..715697d
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2021 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.autofill;
+
+import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.RemoteException;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.SaveInfo;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Maintains a client suggestions session with the
+ * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}.
+ *
+ */
+final class ClientSuggestionsSession {
+
+    private static final String TAG = "ClientSuggestionsSession";
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS;
+
+    private final int mSessionId;
+    private final IAutoFillManagerClient mClient;
+    private final Handler mHandler;
+    private final ComponentName mComponentName;
+
+    private final RemoteFillService.FillServiceCallbacks mCallbacks;
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private AndroidFuture<FillResponse> mPendingFillRequest;
+    @GuardedBy("mLock")
+    private int mPendingFillRequestId = INVALID_REQUEST_ID;
+
+    ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler,
+            ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) {
+        mSessionId = sessionId;
+        mClient = client;
+        mHandler = handler;
+        mComponentName = componentName;
+        mCallbacks = callbacks;
+    }
+
+    void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) {
+        final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+        final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>();
+        final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>();
+
+        mHandler.post(() -> {
+            if (sVerbose) {
+                Slog.v(TAG, "calling onFillRequest() for id=" + requestId);
+            }
+
+            try {
+                mClient.requestFillFromClient(requestId, inlineRequest,
+                        new FillCallbackImpl(fillRequest, futureRef, cancellationSink));
+            } catch (RemoteException e) {
+                fillRequest.completeExceptionally(e);
+            }
+        });
+
+        fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+        futureRef.set(fillRequest);
+
+        synchronized (mLock) {
+            mPendingFillRequest = fillRequest;
+            mPendingFillRequestId = requestId;
+        }
+
+        fillRequest.whenComplete((res, err) -> mHandler.post(() -> {
+            synchronized (mLock) {
+                mPendingFillRequest = null;
+                mPendingFillRequestId = INVALID_REQUEST_ID;
+            }
+            if (err == null) {
+                processAutofillId(res);
+                mCallbacks.onFillRequestSuccess(requestId, res,
+                        mComponentName.getPackageName(), flags);
+            } else {
+                Slog.e(TAG, "Error calling on  client fill request", err);
+                if (err instanceof TimeoutException) {
+                    dispatchCancellationSignal(cancellationSink.get());
+                    mCallbacks.onFillRequestTimeout(requestId);
+                } else if (err instanceof CancellationException) {
+                    dispatchCancellationSignal(cancellationSink.get());
+                } else {
+                    mCallbacks.onFillRequestFailure(requestId, err.getMessage());
+                }
+            }
+        }));
+    }
+
+    /**
+     * Gets the application info for the component.
+     */
+    @Nullable
+    static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) {
+        try {
+            ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo(
+                    comp.getPackageName(),
+                    PackageManager.GET_META_DATA,
+                    userId);
+            if (si != null) {
+                return si;
+            }
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
+     * Gets the user-visible name of the application.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) {
+        return appInfo == null ? null : appInfo.loadSafeLabel(
+                context.getPackageManager(), 0 /* do not ellipsize */,
+                TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
+    }
+
+    /**
+     * Gets the user-visible icon of the application.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) {
+        return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager());
+    }
+
+    int cancelCurrentRequest() {
+        synchronized (mLock) {
+            return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
+                    ? mPendingFillRequestId
+                    : INVALID_REQUEST_ID;
+        }
+    }
+
+    /**
+     * The {@link AutofillId} which the client gets from its view is not contain the session id,
+     * but Autofill framework is using the {@link AutofillId} with a session id. So before using
+     * those ids in the Autofill framework, applies the current session id.
+     *
+     * @param res which response need to apply for a session id
+     */
+    private void processAutofillId(FillResponse res) {
+        if (res == null) {
+            return;
+        }
+
+        final List<Dataset> datasets = res.getDatasets();
+        if (datasets != null && !datasets.isEmpty()) {
+            for (int i = 0; i < datasets.size(); i++) {
+                final Dataset dataset = datasets.get(i);
+                if (dataset != null) {
+                    applySessionId(dataset.getFieldIds());
+                }
+            }
+        }
+
+        final SaveInfo saveInfo = res.getSaveInfo();
+        if (saveInfo != null) {
+            applySessionId(saveInfo.getOptionalIds());
+            applySessionId(saveInfo.getRequiredIds());
+            applySessionId(saveInfo.getSanitizerValues());
+            applySessionId(saveInfo.getTriggerId());
+        }
+    }
+
+    private void applySessionId(List<AutofillId> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return;
+        }
+
+        for (int i = 0; i < ids.size(); i++) {
+            applySessionId(ids.get(i));
+        }
+    }
+
+    private void applySessionId(AutofillId[][] ids) {
+        if (ids == null) {
+            return;
+        }
+        for (int i = 0; i < ids.length; i++) {
+            applySessionId(ids[i]);
+        }
+    }
+
+    private void applySessionId(AutofillId[] ids) {
+        if (ids == null) {
+            return;
+        }
+        for (int i = 0; i < ids.length; i++) {
+            applySessionId(ids[i]);
+        }
+    }
+
+    private void applySessionId(AutofillId id) {
+        if (id == null) {
+            return;
+        }
+        id.setSessionId(mSessionId);
+    }
+
+    private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
+        if (signal == null) {
+            return;
+        }
+        try {
+            signal.cancel();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error requesting a cancellation", e);
+        }
+    }
+
+    private class FillCallbackImpl extends IFillCallback.Stub {
+        final AndroidFuture<FillResponse> mFillRequest;
+        final AtomicReference<AndroidFuture<FillResponse>> mFutureRef;
+        final AtomicReference<ICancellationSignal> mCancellationSink;
+
+        FillCallbackImpl(AndroidFuture<FillResponse> fillRequest,
+                AtomicReference<AndroidFuture<FillResponse>> futureRef,
+                AtomicReference<ICancellationSignal> cancellationSink) {
+            mFillRequest = fillRequest;
+            mFutureRef = futureRef;
+            mCancellationSink = cancellationSink;
+        }
+
+        @Override
+        public void onCancellable(ICancellationSignal cancellation) {
+            AndroidFuture<FillResponse> future = mFutureRef.get();
+            if (future != null && future.isCancelled()) {
+                dispatchCancellationSignal(cancellation);
+            } else {
+                mCancellationSink.set(cancellation);
+            }
+        }
+
+        @Override
+        public void onSuccess(FillResponse response) {
+            mFillRequest.complete(response);
+        }
+
+        @Override
+        public void onFailure(int requestId, CharSequence message) {
+            String errorMessage = message == null ? "" : String.valueOf(message);
+            mFillRequest.completeExceptionally(
+                    new RuntimeException(errorMessage));
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7e277ba..b0445ae 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -31,6 +31,7 @@
 import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
 
@@ -61,6 +62,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -364,6 +366,9 @@
      */
     private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
 
+    @Nullable
+    private ClientSuggestionsSession mClientSuggestionsSession;
+
     // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
     // new one per Session.
     private final BroadcastReceiver mDelayedFillBroadcastReceiver =
@@ -456,6 +461,9 @@
         /** Whether the current {@link FillResponse} is expired. */
         private boolean mExpiredResponse;
 
+        /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
+        private boolean mClientSuggestionsEnabled;
+
         /** Whether the fill dialog UI is disabled. */
         private boolean mFillDialogDisabled;
     }
@@ -486,14 +494,21 @@
                     }
                     mWaitForInlineRequest = inlineSuggestionsRequest != null;
                     mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
-                    maybeRequestFillLocked();
+                    mWaitForInlineRequest = inlineSuggestionsRequest != null;
+                    maybeRequestFillFromServiceLocked();
                     viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
                 }
             } : null;
         }
 
+        void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
+            mPendingFillRequest = null;
+            mWaitForInlineRequest = inlineRequest != null;
+            mPendingInlineSuggestionsRequest = inlineRequest;
+        }
+
         @GuardedBy("mLock")
-        void maybeRequestFillLocked() {
+        void maybeRequestFillFromServiceLocked() {
             if (mPendingFillRequest == null) {
                 return;
             }
@@ -503,10 +518,14 @@
                     return;
                 }
 
-                mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
-                        mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
-                        mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest,
-                        mPendingFillRequest.getDelayedFillIntentSender());
+                if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
+                    mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+                            mPendingFillRequest.getFillContexts(),
+                            mPendingFillRequest.getClientState(),
+                            mPendingFillRequest.getFlags(),
+                            mPendingInlineSuggestionsRequest,
+                            mPendingFillRequest.getDelayedFillIntentSender());
+                }
             }
             mLastFillRequest = mPendingFillRequest;
 
@@ -618,7 +637,7 @@
                             : mDelayedFillPendingIntent.getIntentSender());
 
                 mPendingFillRequest = request;
-                maybeRequestFillLocked();
+                maybeRequestFillFromServiceLocked();
             }
 
             if (mActivityToken != null) {
@@ -843,30 +862,39 @@
     }
 
     /**
-     * Cancels the last request sent to the {@link #mRemoteFillService}.
+     * Cancels the last request sent to the {@link #mRemoteFillService} or the
+     * {@link #mClientSuggestionsSession}.
      */
     @GuardedBy("mLock")
     private void cancelCurrentRequestLocked() {
-        if (mRemoteFillService == null) {
-            wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
-                    + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+        if (mRemoteFillService == null && mClientSuggestionsSession == null) {
+            wtf(null, "cancelCurrentRequestLocked() called without a remote service or a "
+                    + "client suggestions session.  mForAugmentedAutofillOnly: %s",
+                    mSessionFlags.mAugmentedAutofillOnly);
             return;
         }
-        final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
 
-        // Remove the FillContext as there will never be a response for the service
-        if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
-            final int numContexts = mContexts.size();
+        if (mRemoteFillService != null) {
+            final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
 
-            // It is most likely the last context, hence search backwards
-            for (int i = numContexts - 1; i >= 0; i--) {
-                if (mContexts.get(i).getRequestId() == canceledRequest) {
-                    if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
-                    mContexts.remove(i);
-                    break;
+            // Remove the FillContext as there will never be a response for the service
+            if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+                final int numContexts = mContexts.size();
+
+                // It is most likely the last context, hence search backwards
+                for (int i = numContexts - 1; i >= 0; i--) {
+                    if (mContexts.get(i).getRequestId() == canceledRequest) {
+                        if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+                        mContexts.remove(i);
+                        break;
+                    }
                 }
             }
         }
+
+        if (mClientSuggestionsSession != null) {
+            mClientSuggestionsSession.cancelCurrentRequest();
+        }
     }
 
     private boolean isViewFocusedLocked(int flags) {
@@ -931,17 +959,30 @@
         // structure is taken. This causes only one fill request per burst of focus changes.
         cancelCurrentRequestLocked();
 
-        // Only ask IME to create inline suggestions request if Autofill provider supports it and
-        // the render service is available except the autofill is triggered manually and the view
-        // is also not focused.
+        // Only ask IME to create inline suggestions request when
+        // 1. Autofill provider supports it or client enabled client suggestions.
+        // 2. The render service is available.
+        // 3. The view is focused. (The view may not be focused if the autofill is triggered
+        //    manually.)
         final RemoteInlineSuggestionRenderService remoteRenderService =
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
-        if (mSessionFlags.mInlineSupportedByService
+        if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
                 && remoteRenderService != null
-                && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
-            Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
-                    mAssistReceiver.newAutofillRequestLocked(viewState,
-                            /* isInlineRequest= */ true);
+                && (isViewFocusedLocked(flags) || (isRequestSupportFillDialog(flags)))) {
+            final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
+            if (mSessionFlags.mClientSuggestionsEnabled) {
+                final int finalRequestId = requestId;
+                inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> {
+                    // Using client suggestions
+                    synchronized (mLock) {
+                        onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest);
+                    }
+                    viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+                };
+            } else {
+                inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(
+                        viewState, /* isInlineRequest= */ true);
+            }
             if (inlineSuggestionsRequestConsumer != null) {
                 final AutofillId focusedId = mCurrentViewId;
                 final int requestIdCopy = requestId;
@@ -957,10 +998,18 @@
                 );
                 viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
             }
+        } else if (mSessionFlags.mClientSuggestionsEnabled) {
+            // Request client suggestions for the dropdown mode
+            onClientFillRequestLocked(requestId, null);
         } else {
             mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
         }
 
+        if (mSessionFlags.mClientSuggestionsEnabled) {
+            // Using client suggestions, unnecessary request AssistStructure
+            return;
+        }
+
         // Now request the assist structure data.
         requestAssistStructureLocked(requestId, flags);
     }
@@ -1020,10 +1069,13 @@
         mComponentName = componentName;
         mCompatMode = compatMode;
         mSessionState = STATE_ACTIVE;
+
         synchronized (mLock) {
             mSessionFlags = new SessionFlags();
             mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
             mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
+            mSessionFlags.mClientSuggestionsEnabled =
+                    (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0;
             setClientLocked(client);
         }
 
@@ -1135,12 +1187,13 @@
                 if (requestLog != null) {
                     requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
                 }
-                processNullResponseLocked(requestId, requestFlags);
+                processNullResponseOrFallbackLocked(requestId, requestFlags);
                 return;
             }
 
             fieldClassificationIds = response.getFieldClassificationIds();
-            if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) {
+            if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
+                    && !mService.isFieldClassificationEnabledLocked()) {
                 Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
                 processNullResponseLocked(requestId, requestFlags);
                 return;
@@ -1225,6 +1278,26 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void processNullResponseOrFallbackLocked(int requestId, int flags) {
+        if (!mSessionFlags.mClientSuggestionsEnabled) {
+            processNullResponseLocked(requestId, flags);
+            return;
+        }
+
+        // fallback to the default platform password manager
+        mSessionFlags.mClientSuggestionsEnabled = false;
+
+        final InlineSuggestionsRequest inlineRequest =
+                (mLastInlineSuggestionsRequest != null
+                        && mLastInlineSuggestionsRequest.first == requestId)
+                        ? mLastInlineSuggestionsRequest.second : null;
+        mAssistReceiver.newAutofillRequestLocked(inlineRequest);
+        requestAssistStructureLocked(requestId,
+                flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
+        return;
+    }
+
     // FillServiceCallbacks
     @Override
     public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
@@ -3196,13 +3269,22 @@
             filterText = value.getTextValue().toString();
         }
 
-        final CharSequence serviceLabel;
-        final Drawable serviceIcon;
+        final CharSequence targetLabel;
+        final Drawable targetIcon;
         synchronized (mLock) {
-            serviceLabel = mService.getServiceLabelLocked();
-            serviceIcon = mService.getServiceIconLocked();
+            if (mSessionFlags.mClientSuggestionsEnabled) {
+                final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName,
+                        mService.getUserId());
+                targetLabel = ClientSuggestionsSession.getAppLabelLocked(
+                        mService.getMaster().getContext(), appInfo);
+                targetIcon = ClientSuggestionsSession.getAppIconLocked(
+                        mService.getMaster().getContext(), appInfo);
+            } else {
+                targetLabel = mService.getServiceLabelLocked();
+                targetIcon = mService.getServiceIconLocked();
+            }
         }
-        if (serviceLabel == null || serviceIcon == null) {
+        if (targetLabel == null || targetIcon == null) {
             wtf(null, "onFillReady(): no service label or icon");
             return;
         }
@@ -3233,7 +3315,7 @@
 
         getUiForShowing().showFillUi(filledId, response, filterText,
                 mService.getServicePackageName(), mComponentName,
-                serviceLabel, serviceIcon, this, id, mCompatMode);
+                targetLabel, targetIcon, this, id, mCompatMode);
 
         synchronized (mLock) {
             mService.logDatasetShown(id, mClientState, UI_TYPE_MENU);
@@ -3362,6 +3444,17 @@
             return false;
         }
 
+        final InlineSuggestionsRequest request = inlineSuggestionsRequest.get();
+        if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported()
+                || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) {
+            if (sDebug) {
+                Slog.d(TAG, "Inline suggestions not supported for "
+                        + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service")
+                        + ". Falling back to dropdown.");
+            }
+            return false;
+        }
+
         final RemoteInlineSuggestionRenderService remoteRenderService =
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
         if (remoteRenderService == null) {
@@ -3370,7 +3463,7 @@
         }
 
         final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
-                new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
+                new InlineFillUi.InlineFillUiInfo(request, focusedId,
                         filterText, remoteRenderService, userId, id);
         InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
                 new InlineFillUi.InlineSuggestionUiCallback() {
@@ -3980,6 +4073,25 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void onClientFillRequestLocked(int requestId,
+            InlineSuggestionsRequest inlineSuggestionsRequest) {
+        if (mClientSuggestionsSession == null) {
+            mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler,
+                    mComponentName, this);
+        }
+
+        if (mContexts == null) {
+            mContexts = new ArrayList<>(1);
+        }
+
+        if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
+            inlineSuggestionsRequest = null;
+        }
+
+        mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags);
+    }
+
     /**
      * The result of checking whether to show the save dialog, when session can be saved.
      *
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index e80a6d9..9f0deea 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -235,60 +235,7 @@
             return;
         }
 
-        long homeVersion = 0;
-        ArrayList<byte[]> homeSigHashes = null;
-        PackageInfo homeInfo = null;
-        String homeInstaller = null;
-        ComponentName home = getPreferredHomeComponent();
-        if (home != null) {
-            try {
-                homeInfo = mPackageManager.getPackageInfoAsUser(home.getPackageName(),
-                        PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
-                homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
-                homeVersion = homeInfo.getLongVersionCode();
-                SigningInfo signingInfo = homeInfo.signingInfo;
-                if (signingInfo == null) {
-                    Slog.e(TAG, "Home app has no signing information");
-                } else {
-                    // retrieve the newest sigs to back up
-                    // TODO (b/73988180) use entire signing history in case of rollbacks
-                    Signature[] homeInfoSignatures = signingInfo.getApkContentsSigners();
-                    homeSigHashes = BackupUtils.hashSignatureArray(homeInfoSignatures);
-                }
-            } catch (NameNotFoundException e) {
-                Slog.w(TAG, "Can't access preferred home info");
-                // proceed as though there were no preferred home set
-                home = null;
-            }
-        }
-
         try {
-            // We need to push a new preferred-home-app record if:
-            //    1. the version of the home app has changed since our last backup;
-            //    2. the home app [or absence] we now use differs from the prior state,
-            // OR 3. it looks like we use the same home app + version as before, but
-            //       the signatures don't match so we treat them as different apps.
-            PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-            final boolean needHomeBackup = (homeVersion != mStoredHomeVersion)
-                    || !Objects.equals(home, mStoredHomeComponent)
-                    || (home != null
-                        && !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo, pmi));
-            if (needHomeBackup) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Home preference changed; backing up new state " + home);
-                }
-                if (home != null) {
-                    outputBuffer.reset();
-                    outputBufferStream.writeUTF(home.flattenToString());
-                    outputBufferStream.writeLong(homeVersion);
-                    outputBufferStream.writeUTF(homeInstaller != null ? homeInstaller : "" );
-                    writeSignatureHashArray(outputBufferStream, homeSigHashes);
-                    writeEntity(data, DEFAULT_HOME_KEY, outputBuffer.toByteArray());
-                } else {
-                    data.writeEntityHeader(DEFAULT_HOME_KEY, -1);
-                }
-            }
-
             /*
              * Global metadata:
              *
@@ -403,7 +350,7 @@
         }
 
         // Finally, write the new state blob -- just the list of all apps we handled
-        writeStateFile(mAllPackages, home, homeVersion, homeSigHashes, newState);
+        writeStateFile(mAllPackages, newState);
     }
 
     private static void writeEntity(BackupDataOutput data, String key, byte[] bytes)
@@ -623,8 +570,7 @@
     }
 
     // Util: write out our new backup state file
-    private void writeStateFile(List<PackageInfo> pkgs, ComponentName preferredHome,
-            long homeVersion, ArrayList<byte[]> homeSigHashes, ParcelFileDescriptor stateFile) {
+    private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
         FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
         BufferedOutputStream outbuf = new BufferedOutputStream(outstream);
         DataOutputStream out = new DataOutputStream(outbuf);
@@ -635,14 +581,6 @@
             out.writeUTF(STATE_FILE_HEADER);
             out.writeInt(STATE_FILE_VERSION);
 
-            // If we remembered a preferred home app, record that
-            if (preferredHome != null) {
-                out.writeUTF(DEFAULT_HOME_KEY);
-                out.writeUTF(preferredHome.flattenToString());
-                out.writeLong(homeVersion);
-                writeSignatureHashArray(out, homeSigHashes);
-            }
-
             // Conclude with the metadata block
             out.writeUTF(GLOBAL_METADATA_KEY);
             out.writeInt(Build.VERSION.SDK_INT);
@@ -789,6 +727,8 @@
                                 + Build.VERSION.INCREMENTAL + ")");
                     }
                 } else if (key.equals(DEFAULT_HOME_KEY)) {
+                    // Default home app data is no longer backed up by this agent. This code is
+                    // kept to handle restore of old backups that still contain home app data.
                     String cn = inputBufferStream.readUTF();
                     mRestoredHome = ComponentName.unflattenFromString(cn);
                     mRestoredHomeVersion = inputBufferStream.readLong();
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 1af35af..e886ed0 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -85,7 +85,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SELinux;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
@@ -3986,7 +3985,7 @@
             String callerLogString = "BMS.filterAppsEligibleForBackup";
             TransportConnection transportConnection =
                     mTransportManager.getCurrentTransportClient(callerLogString);
-            List<String> eligibleApps = new LinkedList<>();
+            List<String> eligibleApps = new ArrayList<>();
             for (String packageName : packages) {
                 if (mScheduledBackupEligibility.appIsRunningAndEligibleForBackupWithTransport(
                         transportConnection, packageName)) {
@@ -3996,7 +3995,7 @@
             if (transportConnection != null) {
                 mTransportManager.disposeOfTransportClient(transportConnection, callerLogString);
             }
-            return eligibleApps.toArray(new String[eligibleApps.size()]);
+            return eligibleApps.toArray(new String[0]);
         } finally {
             Binder.restoreCallingIdentity(oldToken);
         }
@@ -4144,6 +4143,24 @@
                 pw.print(" : ");
                 pw.println(entry.packageName);
             }
+            pw.println(userPrefix + "Agent timeouts:");
+            pw.println("    KvBackupAgentTimeoutMillis: "
+                    + mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis());
+            pw.println("    FullBackupAgentTimeoutMillis: "
+                    + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis());
+            pw.println("    SharedBackupAgentTimeoutMillis: "
+                    + mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis());
+            pw.println("    RestoreAgentTimeoutMillis (system): "
+                    + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
+                    Process.FIRST_APPLICATION_UID - 1));
+            pw.println("    RestoreAgentTimeoutMillis: "
+                    + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
+                    Process.FIRST_APPLICATION_UID));
+            pw.println("    RestoreAgentFinishedTimeoutMillis: "
+                    + mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis());
+            pw.println("    QuotaExceededTimeoutMillis: "
+                    + mAgentTimeoutParameters.getQuotaExceededTimeoutMillis());
+
         }
     }
 
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 76df8b9..e78c8d1 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -24,8 +24,10 @@
 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
+import android.annotation.NonNull;
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
+import android.app.backup.BackupAgent;
 import android.app.backup.BackupManager;
 import android.app.backup.FullBackup;
 import android.app.backup.IBackupManagerMonitor;
@@ -38,10 +40,12 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.provider.Settings;
+import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
@@ -57,6 +61,7 @@
 import com.android.server.backup.utils.RestoreUtils;
 import com.android.server.backup.utils.TarBackupReader;
 
+import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -135,6 +140,8 @@
     private boolean mPipesClosed;
     private final BackupEligibilityRules mBackupEligibilityRules;
 
+    private FileMetadata mReadOnlyParent = null;
+
     public FullRestoreEngine(
             UserBackupManagerService backupManagerService, OperationStorage operationStorage,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
@@ -158,6 +165,22 @@
         mBackupEligibilityRules = backupEligibilityRules;
     }
 
+    @VisibleForTesting
+    FullRestoreEngine() {
+        mIsAdbRestore = false;
+        mAllowApks = false;
+        mEphemeralOpToken = 0;
+        mUserId = 0;
+        mBackupEligibilityRules = null;
+        mAgentTimeoutParameters = null;
+        mBuffer = null;
+        mBackupManagerService = null;
+        mOperationStorage = null;
+        mMonitor = null;
+        mMonitorTask = null;
+        mOnlyPackage = null;
+    }
+
     public IBackupAgent getAgent() {
         return mAgent;
     }
@@ -397,6 +420,11 @@
                         okay = false;
                     }
 
+                    if (shouldSkipReadOnlyDir(info)) {
+                        // b/194894879: We don't support restore of read-only dirs.
+                        okay = false;
+                    }
+
                     // At this point we have an agent ready to handle the full
                     // restore data as well as a pipe for sending data to
                     // that agent.  Tell the agent to start reading from the
@@ -573,6 +601,45 @@
         return (info != null);
     }
 
+    boolean shouldSkipReadOnlyDir(FileMetadata info) {
+        if (isValidParent(mReadOnlyParent, info)) {
+            // This file has a read-only parent directory, we shouldn't
+            // restore it.
+            return true;
+        } else {
+            // We're now in a different branch of the file tree, update the parent
+            // value.
+            if (isReadOnlyDir(info)) {
+                // Current directory is read-only. Remember it so that we can skip all
+                // of its contents.
+                mReadOnlyParent = info;
+                Slog.w(TAG, "Skipping restore of " + info.path + " and its contents as "
+                        + "read-only dirs are currently not supported.");
+                return true;
+            } else {
+                mReadOnlyParent = null;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean isValidParent(FileMetadata parentDir, @NonNull FileMetadata childDir) {
+        return parentDir != null
+                && childDir.packageName.equals(parentDir.packageName)
+                && childDir.domain.equals(parentDir.domain)
+                && childDir.path.startsWith(getPathWithTrailingSeparator(parentDir.path));
+    }
+
+    private static String getPathWithTrailingSeparator(String path) {
+        return path.endsWith(File.separator) ? path : path + File.separator;
+    }
+
+    private static boolean isReadOnlyDir(FileMetadata file) {
+        // Check if owner has 'write' bit in the file's mode value (see 'man -7 inode' for details).
+        return file.type == BackupAgent.TYPE_DIRECTORY && (file.mode & OsConstants.S_IWUSR) == 0;
+    }
+
     private void setUpPipes() throws IOException {
         synchronized (mPipesLock) {
             mPipes = ParcelFileDescriptor.createPipe();
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 7a5fa62..47d2640 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -26,6 +26,7 @@
 import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
 import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
 import static com.android.server.companion.RolesUtils.isRoleHolder;
+import static com.android.server.companion.Utils.prepareForIpc;
 
 import static java.util.Objects.requireNonNull;
 
@@ -47,7 +48,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
@@ -413,20 +413,4 @@
 
         return requestingPackageSignatureAllowlisted;
     }
-
-    /**
-     * Convert an instance of a "locally-defined" ResultReceiver to an instance of
-     * {@link android.os.ResultReceiver} itself, which the receiving process will be able to
-     * unmarshall.
-     */
-    private static <T extends ResultReceiver> ResultReceiver prepareForIpc(T resultReceiver) {
-        final Parcel parcel = Parcel.obtain();
-        resultReceiver.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-
-        final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel);
-        parcel.recycle();
-
-        return ipcFriendly;
-    }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 02a0a95..e06cd46 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -97,6 +97,8 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
+import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
 import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
 import com.android.server.pm.UserManagerInternal;
 
@@ -128,7 +130,9 @@
     private final PersistUserStateHandler mUserPersistenceHandler;
 
     private final AssociationStoreImpl mAssociationStore;
+    private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
     private AssociationRequestsProcessor mAssociationRequestsProcessor;
+    private SystemDataTransferProcessor mSystemDataTransferProcessor;
     private CompanionDevicePresenceMonitor mDevicePresenceMonitor;
     private CompanionApplicationController mCompanionAppController;
 
@@ -162,6 +166,7 @@
 
         mUserPersistenceHandler = new PersistUserStateHandler();
         mAssociationStore = new AssociationStoreImpl();
+        mSystemDataTransferRequestStore = new SystemDataTransferRequestStore();
     }
 
     @Override
@@ -176,6 +181,8 @@
 
         mAssociationRequestsProcessor = new AssociationRequestsProcessor(
                 /* cdmService */this, mAssociationStore);
+        mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
+                mSystemDataTransferRequestStore);
 
         final Context context = getContext();
         mCompanionAppController = new CompanionApplicationController(
@@ -600,6 +607,18 @@
         }
 
         @Override
+        public PendingIntent buildPermissionTransferUserConsentIntent(String packageName,
+                int userId, int associationId) throws RemoteException {
+            return mSystemDataTransferProcessor.buildPermissionTransferUserConsentIntent(
+                    packageName, userId, associationId);
+        }
+
+        @Override
+        public void startSystemDataTransfer(int userId, int associationId) throws RemoteException {
+            // TODO(b/222121838)
+        }
+
+        @Override
         public void notifyDeviceAppeared(int associationId) {
             if (DEBUG) Log.i(TAG, "notifyDevice_Appeared() id=" + associationId);
 
@@ -857,6 +876,9 @@
         // Removing the association.
         mAssociationStore.removeAssociation(associationId);
 
+        // Remove all the system data transfer requests for the association.
+        mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId);
+
         final List<AssociationInfo> otherAssociations =
                 mAssociationStore.getAssociationsForPackage(userId, packageName);
 
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
index 8ac741a..73e68ec 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -33,15 +33,24 @@
 import java.io.File;
 import java.io.FileOutputStream;
 
-final class DataStoreUtils {
+/**
+ * Util class for CDM data stores
+ */
+public final class DataStoreUtils {
     private static final String TAG = "CompanionDevice_DataStoreUtils";
 
-    static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+    /**
+     * Check if the parser pointer is at the start of the tag
+     */
+    public static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
             throws XmlPullParserException {
         return parser.getEventType() == START_TAG && tag.equals(parser.getName());
     }
 
-    static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+    /**
+     * Check if the parser pointer is at the end of the tag
+     */
+    public static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
             throws XmlPullParserException {
         return parser.getEventType() == END_TAG && tag.equals(parser.getName());
     }
@@ -57,7 +66,7 @@
      * @return an AtomicFile for the user
      */
     @NonNull
-    static AtomicFile createStorageFileForUser(@UserIdInt int userId, String fileName) {
+    public static AtomicFile createStorageFileForUser(@UserIdInt int userId, String fileName) {
         return new AtomicFile(getBaseStorageFileForUser(userId, fileName));
     }
 
@@ -70,7 +79,7 @@
      * Writing to file could fail, for example, if the user has been recently removed and so was
      * their DE (/data/system_de/<user-id>/) directory.
      */
-    static void writeToFileSafely(
+    public static void writeToFileSafely(
             @NonNull AtomicFile file, @NonNull ThrowingConsumer<FileOutputStream> consumer) {
         try {
             file.write(consumer);
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
index a2b2059..42c7687 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -41,8 +41,8 @@
 
 import com.android.internal.util.ArrayUtils;
 
+import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -87,7 +87,8 @@
         final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser(
                 COMPANION_SERVICE_INTENT, ResolveInfoFlags.of(0), userId);
 
-        final Map<String, List<ComponentName>> packageNameToServiceInfoList = new HashMap<>();
+        final Map<String, List<ComponentName>> packageNameToServiceInfoList =
+                new HashMap<>(companionServices.size());
 
         for (ResolveInfo resolveInfo : companionServices) {
             final ServiceInfo service = resolveInfo.serviceInfo;
@@ -101,19 +102,19 @@
                 continue;
             }
 
-            // Use LinkedList, because we'll need to prepend "primary" services, while appending the
-            // other (non-primary) services to the list.
-            final LinkedList<ComponentName> services =
-                    (LinkedList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent(
-                            service.packageName, it -> new LinkedList<>());
+            // We'll need to prepend "primary" services, while appending the other (non-primary)
+            // services to the list.
+            final ArrayList<ComponentName> services =
+                    (ArrayList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent(
+                            service.packageName, it -> new ArrayList<>(1));
 
             final ComponentName componentName = service.getComponentName();
 
             if (isPrimaryCompanionDeviceService(pm, componentName)) {
                 // "Primary" service should be at the head of the list.
-                services.addFirst(componentName);
+                services.add(0, componentName);
             } else {
-                services.addLast(componentName);
+                services.add(componentName);
             }
         }
 
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 3639389..4d42838 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -103,7 +103,7 @@
  * Since Android T the data is stored to "companion_device_manager.xml" file in
  * {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}.
  *
- * See {@link #getBaseStorageFileForUser(int) getBaseStorageFileForUser()}
+ * See {@link #getStorageFileForUser(int)}
  *
  * <p>
  * Since Android T the data is stored using the v1 schema.
diff --git a/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java b/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java
deleted file mode 100644
index 38e5d16..0000000
--- a/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion;
-
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readThisListXml;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeListXml;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.companion.SystemDataTransferRequest;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
-import android.util.Xml;
-
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-/**
- * The class is responsible for reading/writing SystemDataTransferRequest records from/to the disk.
- *
- * The following snippet is a sample XML file stored in the disk.
- * <pre>{@code
- * <requests>
- *   <request
- *     association_id="1"
- *     is_permission_sync_all_packages="false">
- *     <list name="permission_sync_packages">
- *       <string>com.sample.app1</string>
- *       <string>com.sample.app2</string>
- *     </list>
- *   </request>
- * </requests>
- * }</pre>
- */
-public class SystemDataTransferRequestDataStore {
-
-    private static final String LOG_TAG = SystemDataTransferRequestDataStore.class.getSimpleName();
-
-    private static final String FILE_NAME = "companion_device_system_data_transfer_requests.xml";
-
-    private static final String XML_TAG_REQUESTS = "requests";
-    private static final String XML_TAG_REQUEST = "request";
-    private static final String XML_TAG_LIST = "list";
-
-    private static final String XML_ATTR_ASSOCIATION_ID = "association_id";
-    private static final String XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES =
-            "is_permission_sync_all_packages";
-    private static final String XML_ATTR_PERMISSION_SYNC_PACKAGES = "permission_sync_packages";
-
-    private final ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
-            new ConcurrentHashMap<>();
-
-    /**
-     * Reads previously persisted data for the given user
-     *
-     * @param userId Android UserID
-     * @return a list of SystemDataTransferRequest
-     */
-    @NonNull
-    List<SystemDataTransferRequest> readRequestsForUser(@UserIdInt int userId) {
-        final AtomicFile file = getStorageFileForUser(userId);
-        Slog.i(LOG_TAG, "Reading SystemDataTransferRequests for user " + userId + " from "
-                + "file=" + file.getBaseFile().getPath());
-
-        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
-        // accesses to the file on the file system using this AtomicFile object.
-        synchronized (file) {
-            if (!file.getBaseFile().exists()) {
-                Slog.d(LOG_TAG, "File does not exist -> Abort");
-                return Collections.emptyList();
-            }
-            try (FileInputStream in = file.openRead()) {
-                final TypedXmlPullParser parser = Xml.resolvePullParser(in);
-                XmlUtils.beginDocument(parser, XML_TAG_REQUESTS);
-
-                return readRequests(parser);
-            } catch (XmlPullParserException | IOException e) {
-                Slog.e(LOG_TAG, "Error while reading requests file", e);
-                return Collections.emptyList();
-            }
-        }
-    }
-
-    @NonNull
-    private List<SystemDataTransferRequest> readRequests(@NonNull TypedXmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        if (!isStartOfTag(parser, XML_TAG_REQUESTS)) {
-            throw new XmlPullParserException("The XML doesn't have start tag: " + XML_TAG_REQUESTS);
-        }
-
-        List<SystemDataTransferRequest> requests = new ArrayList<>();
-
-        while (true) {
-            parser.nextTag();
-            if (isEndOfTag(parser, XML_TAG_REQUESTS)) break;
-            if (isStartOfTag(parser, XML_TAG_REQUEST)) {
-                requests.add(readRequest(parser));
-            }
-        }
-
-        return requests;
-    }
-
-    private SystemDataTransferRequest readRequest(@NonNull TypedXmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        if (!isStartOfTag(parser, XML_TAG_REQUEST)) {
-            throw new XmlPullParserException("XML doesn't have start tag: " + XML_TAG_REQUEST);
-        }
-
-        final int associationId = readIntAttribute(parser, XML_ATTR_ASSOCIATION_ID);
-        final boolean isPermissionSyncAllPackages = readBooleanAttribute(parser,
-                XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES);
-        parser.nextTag();
-        List<String> permissionSyncPackages = new ArrayList<>();
-        if (isStartOfTag(parser, XML_TAG_LIST)) {
-            parser.nextTag();
-            permissionSyncPackages = readThisListXml(parser, XML_TAG_LIST,
-                    new String[1]);
-        }
-
-        return new SystemDataTransferRequest(associationId, isPermissionSyncAllPackages,
-                permissionSyncPackages);
-    }
-
-    /**
-     * Persisted user's SystemDataTransferRequest data to the disk.
-     *
-     * @param userId   Android UserID
-     * @param requests a list of user's SystemDataTransferRequest.
-     */
-    void writeRequestsForUser(@UserIdInt int userId,
-            @NonNull List<SystemDataTransferRequest> requests) {
-        final AtomicFile file = getStorageFileForUser(userId);
-        Slog.i(LOG_TAG, "Writing SystemDataTransferRequests for user " + userId + " to file="
-                + file.getBaseFile().getPath());
-
-        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
-        // accesses to the file on the file system using this AtomicFile object.
-        synchronized (file) {
-            writeToFileSafely(file, out -> {
-                final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
-                serializer.setFeature(
-                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
-                serializer.startDocument(null, true);
-                writeRequests(serializer, requests);
-                serializer.endDocument();
-            });
-        }
-    }
-
-    private void writeRequests(@NonNull TypedXmlSerializer serializer,
-            @Nullable Collection<SystemDataTransferRequest> requests) throws IOException {
-        serializer.startTag(null, XML_TAG_REQUESTS);
-
-        for (SystemDataTransferRequest request : requests) {
-            writeRequest(serializer, request);
-        }
-
-        serializer.endTag(null, XML_TAG_REQUESTS);
-    }
-
-    private void writeRequest(@NonNull TypedXmlSerializer serializer,
-            @NonNull SystemDataTransferRequest request) throws IOException {
-        serializer.startTag(null, XML_TAG_REQUEST);
-
-        writeIntAttribute(serializer, XML_ATTR_ASSOCIATION_ID, request.getAssociationId());
-        writeBooleanAttribute(serializer, XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES,
-                request.isPermissionSyncAllPackages());
-        try {
-            writeListXml(request.getPermissionSyncPackages(), XML_ATTR_PERMISSION_SYNC_PACKAGES,
-                    serializer);
-        } catch (XmlPullParserException e) {
-            Slog.e(LOG_TAG, "Error writing permission sync packages into XML. "
-                    + request.getPermissionSyncPackages().toString());
-        }
-
-        serializer.endTag(null, XML_TAG_REQUEST);
-    }
-
-    /**
-     * Creates and caches {@link AtomicFile} object that represents the back-up file for the given
-     * user.
-     *
-     * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
-     * possible to synchronize reads and writes to the file using the returned object.
-     */
-    private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
-        return mUserIdToStorageFile.computeIfAbsent(userId,
-                u -> createStorageFileForUser(userId, FILE_NAME));
-    }
-}
diff --git a/services/companion/java/com/android/server/companion/Utils.java b/services/companion/java/com/android/server/companion/Utils.java
new file mode 100644
index 0000000..b9f61ec
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/Utils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import android.os.Parcel;
+import android.os.ResultReceiver;
+
+/**
+ * A miscellaneous util class for CDM
+ *
+ * @hide
+ */
+public final class Utils {
+
+    /**
+     * Convert an instance of a "locally-defined" ResultReceiver to an instance of
+     * {@link android.os.ResultReceiver} itself, which the receiving process will be able to
+     * unmarshall.
+     * @hide
+     */
+    public static <T extends ResultReceiver> ResultReceiver prepareForIpc(T resultReceiver) {
+        final Parcel parcel = Parcel.obtain();
+        resultReceiver.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        return ipcFriendly;
+    }
+
+    private Utils() {}
+}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
new file mode 100644
index 0000000..ca47453
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer;
+
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME;
+import static android.content.ComponentName.createRelative;
+
+import static com.android.server.companion.Utils.prepareForIpc;
+
+import android.annotation.UserIdInt;
+import android.app.PendingIntent;
+import android.companion.AssociationInfo;
+import android.companion.DeviceNotAssociatedException;
+import android.companion.datatransfer.PermissionSyncRequest;
+import android.companion.datatransfer.SystemDataTransferRequest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Slog;
+
+import com.android.server.companion.AssociationStore;
+import com.android.server.companion.CompanionDeviceManagerService;
+
+import java.util.List;
+
+/**
+ * This processor builds user consent intent for a given SystemDataTransferRequest and processes the
+ * request when the system is ready (a secure channel is established between the handhold and the
+ * companion device).
+ */
+public class SystemDataTransferProcessor {
+
+    private static final String LOG_TAG = SystemDataTransferProcessor.class.getSimpleName();
+
+    // Values from UI to SystemDataTransferProcessor via ResultReceiver
+    private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0;
+    private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED = 1;
+    private static final String EXTRA_PERMISSION_SYNC_REQUEST = "permission_sync_request";
+    private static final String EXTRA_COMPANION_DEVICE_NAME = "companion_device_name";
+    private static final String EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER =
+            "system_data_transfer_result_receiver";
+    private static final ComponentName SYSTEM_DATA_TRANSFER_REQUEST_APPROVAL_ACTIVITY =
+            createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
+                    ".CompanionDeviceDataTransferActivity");
+
+    private final Context mContext;
+    private final AssociationStore mAssociationStore;
+    private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
+
+    public SystemDataTransferProcessor(CompanionDeviceManagerService service,
+            AssociationStore associationStore,
+            SystemDataTransferRequestStore systemDataTransferRequestStore) {
+        mContext = service.getContext();
+        mAssociationStore = associationStore;
+        mSystemDataTransferRequestStore = systemDataTransferRequestStore;
+    }
+
+    /**
+     * Build a PendingIntent of permission sync user consent dialog
+     */
+    public PendingIntent buildPermissionTransferUserConsentIntent(String packageName,
+            @UserIdInt int userId, int associationId) throws RemoteException {
+        // The association must exist and either belong to the calling package,
+        // or the calling package must hold REQUEST_SYSTEM_DATA_TRANSFER permission.
+        AssociationInfo association = mAssociationStore.getAssociationById(associationId);
+        if (association == null) {
+            throw new RemoteException(new DeviceNotAssociatedException(
+                    "Association id: " + associationId + " doesn't exist."));
+        } else {
+            if (!association.getPackageName().equals(packageName)) {
+                Slog.e(LOG_TAG, "The calling package doesn't own the association.");
+                return null;
+            }
+
+            // Check if the request's data type has been requested before.
+            List<SystemDataTransferRequest> storedRequests =
+                    mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
+                            associationId);
+            for (SystemDataTransferRequest storedRequest : storedRequests) {
+                if (storedRequest instanceof PermissionSyncRequest) {
+                    Slog.e(LOG_TAG, "The request has been sent before, you can not send "
+                            + "the same request type again.");
+                    return null;
+                }
+            }
+        }
+
+        Slog.i(LOG_TAG, "Creating permission sync intent for userId=" + userId
+                + "associationId: " + associationId);
+
+        // Create an internal intent to launch the user consent dialog
+        final Bundle extras = new Bundle();
+        PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+        request.setUserId(userId);
+        extras.putParcelable(EXTRA_PERMISSION_SYNC_REQUEST, request);
+        extras.putCharSequence(EXTRA_COMPANION_DEVICE_NAME, association.getDisplayName());
+        extras.putParcelable(EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER,
+                prepareForIpc(mOnSystemDataTransferRequestConfirmationReceiver));
+
+        final Intent intent = new Intent();
+        intent.setComponent(SYSTEM_DATA_TRANSFER_REQUEST_APPROVAL_ACTIVITY);
+        intent.putExtras(extras);
+
+        // Create a PendingIntent
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return PendingIntent.getActivity(mContext, /*requestCode */ associationId, intent,
+                    FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private final ResultReceiver mOnSystemDataTransferRequestConfirmationReceiver =
+            new ResultReceiver(Handler.getMain()) {
+                @Override
+                protected void onReceiveResult(int resultCode, Bundle data) {
+                    Slog.d(LOG_TAG, "onReceiveResult() code=" + resultCode + ", "
+                            + "data=" + data);
+
+                    if (resultCode == RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED
+                            || resultCode == RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED) {
+                        final PermissionSyncRequest request =
+                                data.getParcelable(EXTRA_PERMISSION_SYNC_REQUEST,
+                                        PermissionSyncRequest.class);
+                        if (request != null) {
+                            request.setUserConsented(
+                                    resultCode == RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED);
+                            Slog.i(LOG_TAG, "Recording request: " + request);
+                            mSystemDataTransferRequestStore.writeRequest(request.getUserId(),
+                                    request);
+                        }
+
+                        return;
+                    }
+
+                    Slog.e(LOG_TAG, "Unknown result code:" + resultCode);
+                }
+            };
+}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
new file mode 100644
index 0000000..ab0a062
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer;
+
+import static android.companion.datatransfer.SystemDataTransferRequest.DATA_TYPE_PERMISSION_SYNC;
+
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.datatransfer.PermissionSyncRequest;
+import android.companion.datatransfer.SystemDataTransferRequest;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * The class is responsible for reading/writing SystemDataTransferRequest records from/to the disk.
+ * <p>
+ * The following snippet is a sample XML file stored in the disk.
+ * <pre>{@code
+ * <requests>
+ *   <request
+ *     association_id="1"
+ *     data_type="1"
+ *     user_id="12"
+ *     is_user_consented="true"
+ *   </request>
+ * </requests>
+ * }</pre>
+ */
+public class SystemDataTransferRequestStore {
+
+    private static final String LOG_TAG = SystemDataTransferRequestStore.class.getSimpleName();
+
+    private static final String FILE_NAME = "companion_device_system_data_transfer_requests.xml";
+
+    private static final String XML_TAG_REQUESTS = "requests";
+    private static final String XML_TAG_REQUEST = "request";
+
+    private static final String XML_ATTR_ASSOCIATION_ID = "association_id";
+    private static final String XML_ATTR_DATA_TYPE = "data_type";
+    private static final String XML_ATTR_USER_ID = "user_id";
+    private static final String XML_ATTR_IS_USER_CONSENTED = "is_user_consented";
+
+    private static final int READ_FROM_DISK_TIMEOUT = 5; // in seconds
+
+    private final ExecutorService mExecutor;
+    private final ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
+            new ConcurrentHashMap<>();
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final SparseArray<ArrayList<SystemDataTransferRequest>> mCachedPerUser =
+            new SparseArray<>();
+
+    public SystemDataTransferRequestStore() {
+        mExecutor = Executors.newSingleThreadExecutor();
+    }
+
+    @NonNull
+    List<SystemDataTransferRequest> readRequestsByAssociationId(@UserIdInt int userId,
+            int associationId) {
+        List<SystemDataTransferRequest> cachedRequests;
+        synchronized (mLock) {
+            cachedRequests = readRequestsFromCache(userId);
+        }
+
+        List<SystemDataTransferRequest> requestsByAssociationId = new ArrayList<>();
+        for (SystemDataTransferRequest request : cachedRequests) {
+            if (request.getAssociationId() == associationId) {
+                requestsByAssociationId.add(request);
+            }
+        }
+        return requestsByAssociationId;
+    }
+
+    void writeRequest(@UserIdInt int userId, SystemDataTransferRequest request) {
+        Slog.i(LOG_TAG, "Writing request=" + request + " to store.");
+        ArrayList<SystemDataTransferRequest> cachedRequests;
+        synchronized (mLock) {
+            // Write to cache
+            cachedRequests = readRequestsFromCache(userId);
+            cachedRequests.add(request);
+            mCachedPerUser.set(userId, cachedRequests);
+        }
+        // Write to store
+        mExecutor.execute(() -> writeRequestsToStore(userId, cachedRequests));
+    }
+
+    /**
+     * Remove requests by association id. userId must be the one which owns the associationId.
+     */
+    public void removeRequestsByAssociationId(@UserIdInt int userId, int associationId) {
+        Slog.i(LOG_TAG, "Removing system data transfer requests for userId=" + userId
+                + ", associationId=" + associationId);
+        ArrayList<SystemDataTransferRequest> cachedRequests;
+        synchronized (mLock) {
+            // Remove requests from cache
+            cachedRequests = readRequestsFromCache(userId);
+            cachedRequests.removeIf(request -> request.getAssociationId() == associationId);
+            mCachedPerUser.set(userId, cachedRequests);
+        }
+        // Remove requests from store
+        mExecutor.execute(() -> writeRequestsToStore(userId, cachedRequests));
+    }
+
+    @GuardedBy("mLock")
+    private ArrayList<SystemDataTransferRequest> readRequestsFromCache(@UserIdInt int userId) {
+        ArrayList<SystemDataTransferRequest> cachedRequests = mCachedPerUser.get(userId);
+        if (cachedRequests == null) {
+            Future<ArrayList<SystemDataTransferRequest>> future =
+                    mExecutor.submit(() -> readRequestsFromStore(userId));
+            try {
+                cachedRequests = future.get(READ_FROM_DISK_TIMEOUT, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                Slog.e(LOG_TAG, "Thread reading SystemDataTransferRequest from disk is "
+                        + "interrupted.");
+            } catch (ExecutionException e) {
+                Slog.e(LOG_TAG, "Error occurred while reading SystemDataTransferRequest "
+                        + "from disk.");
+            } catch (TimeoutException e) {
+                Slog.e(LOG_TAG, "Reading SystemDataTransferRequest from disk timed out.");
+            }
+            mCachedPerUser.set(userId, cachedRequests);
+        }
+        return cachedRequests;
+    }
+
+    /**
+     * Reads previously persisted data for the given user
+     *
+     * @param userId Android UserID
+     * @return a list of SystemDataTransferRequest
+     */
+    @NonNull
+    private ArrayList<SystemDataTransferRequest> readRequestsFromStore(@UserIdInt int userId) {
+        final AtomicFile file = getStorageFileForUser(userId);
+        Slog.i(LOG_TAG, "Reading SystemDataTransferRequests for user " + userId + " from "
+                + "file=" + file.getBaseFile().getPath());
+
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
+        synchronized (file) {
+            if (!file.getBaseFile().exists()) {
+                Slog.d(LOG_TAG, "File does not exist -> Abort");
+                return new ArrayList<>();
+            }
+            try (FileInputStream in = file.openRead()) {
+                final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+                XmlUtils.beginDocument(parser, XML_TAG_REQUESTS);
+
+                return readRequestsFromXml(parser);
+            } catch (XmlPullParserException | IOException e) {
+                Slog.e(LOG_TAG, "Error while reading requests file", e);
+                return new ArrayList<>();
+            }
+        }
+    }
+
+    @NonNull
+    private ArrayList<SystemDataTransferRequest> readRequestsFromXml(
+            @NonNull TypedXmlPullParser parser) throws XmlPullParserException, IOException {
+        if (!isStartOfTag(parser, XML_TAG_REQUESTS)) {
+            throw new XmlPullParserException("The XML doesn't have start tag: " + XML_TAG_REQUESTS);
+        }
+
+        ArrayList<SystemDataTransferRequest> requests = new ArrayList<>();
+
+        while (true) {
+            parser.nextTag();
+            if (isEndOfTag(parser, XML_TAG_REQUESTS)) {
+                break;
+            }
+            if (isStartOfTag(parser, XML_TAG_REQUEST)) {
+                requests.add(readRequestFromXml(parser));
+            }
+        }
+
+        return requests;
+    }
+
+    private SystemDataTransferRequest readRequestFromXml(@NonNull TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        if (!isStartOfTag(parser, XML_TAG_REQUEST)) {
+            throw new XmlPullParserException("XML doesn't have start tag: " + XML_TAG_REQUEST);
+        }
+
+        final int associationId = readIntAttribute(parser, XML_ATTR_ASSOCIATION_ID);
+        final int dataType = readIntAttribute(parser, XML_ATTR_DATA_TYPE);
+        final int userId = readIntAttribute(parser, XML_ATTR_USER_ID);
+        final boolean isUserConsented = readBooleanAttribute(parser, XML_ATTR_IS_USER_CONSENTED);
+
+        switch (dataType) {
+            case DATA_TYPE_PERMISSION_SYNC:
+                PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+                request.setUserId(userId);
+                request.setUserConsented(isUserConsented);
+                return request;
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Persisted user's SystemDataTransferRequest data to the disk.
+     *
+     * @param userId   Android UserID
+     * @param requests a list of user's SystemDataTransferRequest.
+     */
+    void writeRequestsToStore(@UserIdInt int userId,
+            @NonNull List<SystemDataTransferRequest> requests) {
+        final AtomicFile file = getStorageFileForUser(userId);
+        Slog.i(LOG_TAG, "Writing SystemDataTransferRequests for user " + userId + " to file="
+                + file.getBaseFile().getPath());
+
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
+        synchronized (file) {
+            writeToFileSafely(file, out -> {
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
+                serializer.setFeature(
+                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+                serializer.startDocument(null, true);
+                writeRequestsToXml(serializer, requests);
+                serializer.endDocument();
+            });
+        }
+    }
+
+    private void writeRequestsToXml(@NonNull TypedXmlSerializer serializer,
+            @Nullable Collection<SystemDataTransferRequest> requests) throws IOException {
+        serializer.startTag(null, XML_TAG_REQUESTS);
+
+        for (SystemDataTransferRequest request : requests) {
+            writeRequestToXml(serializer, request);
+        }
+
+        serializer.endTag(null, XML_TAG_REQUESTS);
+    }
+
+    private void writeRequestToXml(@NonNull TypedXmlSerializer serializer,
+            @NonNull SystemDataTransferRequest request) throws IOException {
+        serializer.startTag(null, XML_TAG_REQUEST);
+
+        writeIntAttribute(serializer, XML_ATTR_ASSOCIATION_ID, request.getAssociationId());
+        writeIntAttribute(serializer, XML_ATTR_DATA_TYPE, request.getDataType());
+        writeIntAttribute(serializer, XML_ATTR_USER_ID, request.getUserId());
+        writeBooleanAttribute(serializer, XML_ATTR_IS_USER_CONSENTED, request.isUserConsented());
+
+        serializer.endTag(null, XML_TAG_REQUEST);
+    }
+
+    /**
+     * Creates and caches {@link AtomicFile} object that represents the back-up file for the given
+     * user.
+     * <p>
+     * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+     * possible to synchronize reads and writes to the file using the returned object.
+     */
+    @NonNull
+    private AtomicFile getStorageFileForUser(@UserIdInt int userId) {
+        return mUserIdToStorageFile.computeIfAbsent(userId,
+                u -> createStorageFileForUser(userId, FILE_NAME));
+    }
+}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 06f698e..58c22a75 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -552,18 +552,23 @@
     /**
      * Set which overlay to use for a package.
      * @param userId The user for which to update the overlays.
-     * @param targetPackageName The package name of the package for which to update the overlays.
-     * @param overlayPaths  The complete list of overlay paths that should be enabled for
+     * @param pendingChanges is a map to describe all overlay targets and their related overlay
+     *                      paths. Its key is the overlay target package and its value is the
+     *                      complete list of overlay paths that should be enabled for
      *                      the target. Previously enabled overlays not specified in the list
      *                      will be disabled. Pass in null or empty paths to disable all overlays.
      *                      The order of the items is significant if several overlays modify the
-     *                      same resource.
+     *                      same resource. To pass the concrete ArrayMap type is to reduce the
+     *                      overheads of system server.
      * @param outUpdatedPackageNames An output list that contains the package names of packages
      *                               affected by the update of enabled overlays.
-     * @return true if all packages names were known by the package manager, false otherwise
+     * @param outInvalidPackageNames An output list that contains the package names of packages
+     *                               are not valid.
      */
-    public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
-            @Nullable OverlayPaths overlayPaths, Set<String> outUpdatedPackageNames);
+    public abstract void setEnabledOverlayPackages(int userId,
+            @NonNull ArrayMap<String, OverlayPaths> pendingChanges,
+            @NonNull Set<String> outUpdatedPackageNames,
+            @NonNull Set<String> outInvalidPackageNames);
 
     /**
      * Resolves an activity intent, allowing instant apps to be resolved.
diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java
index c4e84a4..a9bdf06 100644
--- a/services/core/java/com/android/server/ConsumerIrService.java
+++ b/services/core/java/com/android/server/ConsumerIrService.java
@@ -16,6 +16,10 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.TRANSMIT_IR;
+
+import android.annotation.EnforcePermission;
+import android.annotation.RequiresNoPermission;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.IConsumerIrService;
@@ -60,6 +64,7 @@
     }
 
     @Override
+    @RequiresNoPermission
     public boolean hasIrEmitter() {
         return mHasNativeHal;
     }
@@ -85,12 +90,8 @@
 
 
     @Override
+    @EnforcePermission(TRANSMIT_IR)
     public void transmit(String packageName, int carrierFrequency, int[] pattern) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TRANSMIT_IR)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires TRANSMIT_IR permission");
-        }
-
         long totalXmitTime = 0;
 
         for (int slice : pattern) {
@@ -125,12 +126,8 @@
     }
 
     @Override
+    @EnforcePermission(TRANSMIT_IR)
     public int[] getCarrierFrequencies() {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TRANSMIT_IR)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires TRANSMIT_IR permission");
-        }
-
         throwIfNoIrEmitter();
 
         synchronized(mHalLock) {
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 78df983..a562afb 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Environment;
 import android.os.SystemClock;
@@ -162,16 +163,17 @@
     /**
      * Returns true if the jar is in a test APEX.
      */
-    private static boolean isJarInTestApex(String pathStr) {
+    private boolean isJarInTestApex(String pathStr) {
         Path path = Paths.get(pathStr);
         if (path.getNameCount() >= 2 && path.getName(0).toString().equals("apex")) {
             String apexModuleName = path.getName(1).toString();
             ApexManager apexManager = ApexManager.getInstance();
             String packageName = apexManager.getActivePackageNameForApexModuleName(apexModuleName);
-            PackageInfo packageInfo = apexManager.getPackageInfo(
-                    packageName, ApexManager.MATCH_ACTIVE_PACKAGE);
-            if (packageInfo != null) {
+            try {
+                PackageInfo packageInfo =  mContext.getPackageManager().getPackageInfo(packageName,
+                        PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
                 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0;
+            } catch (Exception ignore) {
             }
         }
         return false;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index b059cc7..2c465f4 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1820,6 +1820,14 @@
         if (account == null) {
             return false;
         }
+        if (account.name != null && account.name.length() > 200) {
+            Log.w(TAG, "Account cannot be added - Name longer than 200 chars");
+            return false;
+        }
+        if (account.type != null && account.type.length() > 200) {
+            Log.w(TAG, "Account cannot be added - Name longer than 200 chars");
+            return false;
+        }
         if (!isLocalUnlockedUser(accounts.userId)) {
             Log.w(TAG, "Account " + account.toSafeString() + " cannot be added - user "
                     + accounts.userId + " is locked. callingUid=" + callingUid);
@@ -2065,6 +2073,10 @@
                 + ", pid " + Binder.getCallingPid());
         }
         if (accountToRename == null) throw new IllegalArgumentException("account is null");
+        if (newName != null && newName.length() > 200) {
+            Log.e(TAG, "renameAccount failed - account name longer than 200");
+            throw new IllegalArgumentException("account name longer than 200");
+        }
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
             String msg = String.format(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6109bd4..2af0831 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8731,8 +8731,8 @@
             sb.append("Foreground: ")
                     .append(process.isInterestingToUserLocked() ? "Yes" : "No")
                     .append("\n");
-            if (process.getStartTime() > 0) {
-                long runtimeMillis = SystemClock.elapsedRealtime() - process.getStartTime();
+            if (process.getStartUptime() > 0) {
+                long runtimeMillis = SystemClock.uptimeMillis() - process.getStartUptime();
                 sb.append("Process-Runtime: ").append(runtimeMillis).append("\n");
             }
         }
@@ -15257,7 +15257,6 @@
                 uid, change, procState, procStateSeq, capability, ephemeral);
         if (uidRec != null) {
             uidRec.setLastReportedChange(enqueuedChange);
-            uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
         }
 
         // Directly update the power manager, since we sit on top of it and it is critical
@@ -16523,18 +16522,13 @@
                     return;
                 }
                 record.lastNetworkUpdatedProcStateSeq = procStateSeq;
-                if (record.curProcStateSeq > procStateSeq) {
-                    if (DEBUG_NETWORK) {
-                        Slog.d(TAG_NETWORK, "No need to handle older seq no., Uid: " + uid
-                                + ", curProcstateSeq: " + record.curProcStateSeq
-                                + ", procStateSeq: " + procStateSeq);
-                    }
-                    return;
-                }
-                if (record.waitingForNetwork) {
+                if (record.procStateSeqWaitingForNetwork != 0
+                        && procStateSeq >= record.procStateSeqWaitingForNetwork) {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "Notifying all blocking threads for uid: " + uid
-                                + ", procStateSeq: " + procStateSeq);
+                                + ", procStateSeq: " + procStateSeq
+                                + ", procStateSeqWaitingForNetwork: "
+                                + record.procStateSeqWaitingForNetwork);
                     }
                     record.networkStateLock.notifyAll();
                 }
@@ -17279,7 +17273,7 @@
             if (mNetworkPolicyUidObserver != null) {
                 try {
                     mNetworkPolicyUidObserver.onUidStateChanged(uid, PROCESS_STATE_TOP,
-                            mProcessList.getProcStateSeqCounter(), PROCESS_CAPABILITY_ALL);
+                            mProcessList.getNextProcStateSeq(), PROCESS_CAPABILITY_ALL);
                 } catch (RemoteException e) {
                     // Should not happen; call is within the same process
                 }
@@ -17568,23 +17562,6 @@
             }
         }
         synchronized (record.networkStateLock) {
-            if (record.lastDispatchedProcStateSeq < procStateSeq) {
-                if (DEBUG_NETWORK) {
-                    Slog.d(TAG_NETWORK, "Uid state change for seq no. " + procStateSeq + " is not "
-                            + "dispatched to NPMS yet, so don't wait. Uid: " + callingUid
-                            + " lastProcStateSeqDispatchedToObservers: "
-                            + record.lastDispatchedProcStateSeq);
-                }
-                return;
-            }
-            if (record.curProcStateSeq > procStateSeq) {
-                if (DEBUG_NETWORK) {
-                    Slog.d(TAG_NETWORK, "Ignore the wait requests for older seq numbers. Uid: "
-                            + callingUid + ", curProcStateSeq: " + record.curProcStateSeq
-                            + ", procStateSeq: " + procStateSeq);
-                }
-                return;
-            }
             if (record.lastNetworkUpdatedProcStateSeq >= procStateSeq) {
                 if (DEBUG_NETWORK) {
                     Slog.d(TAG_NETWORK, "Network rules have been already updated for seq no. "
@@ -17600,9 +17577,9 @@
                         + " Uid: " + callingUid + " procStateSeq: " + procStateSeq);
                 }
                 final long startTime = SystemClock.uptimeMillis();
-                record.waitingForNetwork = true;
+                record.procStateSeqWaitingForNetwork = procStateSeq;
                 record.networkStateLock.wait(mWaitForNetworkTimeoutMs);
-                record.waitingForNetwork = false;
+                record.procStateSeqWaitingForNetwork = 0;
                 final long totalTime = SystemClock.uptimeMillis() - startTime;
                 if (totalTime >= mWaitForNetworkTimeoutMs || DEBUG_NETWORK) {
                     Slog.w(TAG_NETWORK, "Total time waited for network rules to get updated: "
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bad7782..72a0e1a 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -4781,13 +4781,17 @@
     }
 
     /**
-     * Checks if any uid is coming from background to foreground or vice versa and if so, increments
-     * the {@link UidRecord#curProcStateSeq} corresponding to that uid using global seq counter
-     * {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
+     * Increments the {@link UidRecord#curProcStateSeq} for all uids using global seq counter
+     * {@link ProcessList#mProcStateSeqCounter} and checks if any uid is coming
+     * from background to foreground or vice versa and if so, notifies the app if it needs to block.
      */
     @VisibleForTesting
     @GuardedBy(anyOf = {"mService", "mProcLock"})
     void incrementProcStateSeqAndNotifyAppsLOSP(ActiveUids activeUids) {
+        for (int i = activeUids.size() - 1; i >= 0; --i) {
+            final UidRecord uidRec = activeUids.valueAt(i);
+            uidRec.curProcStateSeq = getNextProcStateSeq();
+        }
         if (mService.mWaitForNetworkTimeoutMs <= 0) {
             return;
         }
@@ -4814,7 +4818,6 @@
                 continue;
             }
             synchronized (uidRec.networkStateLock) {
-                uidRec.curProcStateSeq = ++mProcStateSeqCounter; // TODO: use method
                 if (blockState == NETWORK_STATE_BLOCK) {
                     if (blockingUids == null) {
                         blockingUids = new ArrayList<>();
@@ -4825,7 +4828,7 @@
                         Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking"
                                 + " threads for uid: " + uidRec);
                     }
-                    if (uidRec.waitingForNetwork) {
+                    if (uidRec.procStateSeqWaitingForNetwork != 0) {
                         uidRec.networkStateLock.notifyAll();
                     }
                 }
@@ -4859,8 +4862,8 @@
         }
     }
 
-    long getProcStateSeqCounter() {
-        return mProcStateSeqCounter;
+    long getNextProcStateSeq() {
+        return ++mProcStateSeqCounter;
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index e42dac4..e8c1b54 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -229,7 +229,6 @@
                     validateUid.setCurProcState(item.procState);
                     validateUid.setSetCapability(item.capability);
                     validateUid.setCurCapability(item.capability);
-                    validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 5c78d1e..51568d8 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -99,16 +99,9 @@
     long lastNetworkUpdatedProcStateSeq;
 
     /**
-     * Last seq number for which AcitivityManagerService dispatched uid state change to
-     * NetworkPolicyManagerService.
+     * Indicates if any thread is waiting for network rules to get updated for {@link #mUid}.
      */
-    @GuardedBy("networkStateUpdate")
-    long lastDispatchedProcStateSeq;
-
-    /**
-     * Indicates if any thread is waiting for network rules to get updated for {@link #uid}.
-     */
-    volatile boolean waitingForNetwork;
+    volatile long procStateSeqWaitingForNetwork;
 
     /**
      * Indicates whether this uid has internet permission or not.
@@ -345,18 +338,6 @@
                 mUid) == PackageManager.PERMISSION_GRANTED;
     }
 
-    /**
-     * If the change being dispatched is not CHANGE_GONE (not interested in
-     * these changes), then update the {@link #lastDispatchedProcStateSeq} with
-     * {@link #curProcStateSeq}.
-     */
-    public void updateLastDispatchedProcStateSeq(int changeToDispatch) {
-        if ((changeToDispatch & CHANGE_GONE) == 0) {
-            lastDispatchedProcStateSeq = curProcStateSeq;
-        }
-    }
-
-
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(UidRecordProto.UID, mUid);
@@ -377,7 +358,6 @@
         proto.write(UidRecordProto.ProcStateSequence.CURURENT, curProcStateSeq);
         proto.write(UidRecordProto.ProcStateSequence.LAST_NETWORK_UPDATED,
                 lastNetworkUpdatedProcStateSeq);
-        proto.write(UidRecordProto.ProcStateSequence.LAST_DISPATCHED, lastDispatchedProcStateSeq);
         proto.end(seqToken);
 
         proto.end(token);
@@ -460,8 +440,6 @@
         sb.append(curProcStateSeq);
         sb.append(",");
         sb.append(lastNetworkUpdatedProcStateSeq);
-        sb.append(",");
-        sb.append(lastDispatchedProcStateSeq);
         sb.append(")}");
         return sb.toString();
     }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 361629b..f35d792 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2783,7 +2783,9 @@
             @Nullable IAppOpsCallback permissionPolicyCallback) {
         enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
         verifyIncomingOp(code);
-        verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+        if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+            return;
+        }
 
         ArraySet<ModeCallback> repCbs = null;
         code = AppOpsManager.opToSwitch(code);
@@ -3218,7 +3220,9 @@
     private int checkOperationImpl(int code, int uid, String packageName,
             @Nullable String attributionTag, boolean raw) {
         verifyIncomingOp(code);
-        verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+        if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+            return AppOpsManager.opToDefaultMode(code);
+        }
 
         String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
@@ -3330,8 +3334,7 @@
     }
 
     private boolean isPackageExisted(String packageName) {
-        return LocalServices.getService(PackageManagerInternal.class)
-                .getPackageStateInternal(packageName) != null;
+        return getPackageManagerInternal().getPackageStateInternal(packageName) != null;
     }
 
     /**
@@ -3366,8 +3369,11 @@
 
         verifyIncomingProxyUid(attributionSource);
         verifyIncomingOp(code);
-        verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
-        verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
+        if (!isIncomingPackageValid(proxiedPackageName, UserHandle.getUserId(proxiedUid))
+                || !isIncomingPackageValid(proxyPackageName, UserHandle.getUserId(proxyUid))) {
+            return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, proxiedAttributionTag,
+                    proxiedPackageName);
+        }
 
         skipProxyOperation = skipProxyOperation
                 && isCallerAndAttributionTrusted(attributionSource);
@@ -3424,7 +3430,10 @@
             @Nullable String message, boolean shouldCollectMessage) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+        if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+            return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+                    packageName);
+        }
 
         String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
@@ -3830,7 +3839,10 @@
             int attributionChainId) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+        if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+            return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+                    packageName);
+        }
 
         String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
@@ -3884,8 +3896,11 @@
 
         verifyIncomingProxyUid(attributionSource);
         verifyIncomingOp(code);
-        verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
-        verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
+        if (!isIncomingPackageValid(proxyPackageName, UserHandle.getUserId(proxyUid))
+                || !isIncomingPackageValid(proxiedPackageName, UserHandle.getUserId(proxiedUid))) {
+            return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, proxiedAttributionTag,
+                    proxiedPackageName);
+        }
 
         boolean isCallerTrusted = isCallerAndAttributionTrusted(attributionSource);
         skipProxyOperation = isCallerTrusted && skipProxyOperation;
@@ -4070,7 +4085,9 @@
             String attributionTag) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+        if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+            return;
+        }
 
         String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
@@ -4103,8 +4120,10 @@
 
         verifyIncomingProxyUid(attributionSource);
         verifyIncomingOp(code);
-        verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
-        verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
+        if (!isIncomingPackageValid(proxyPackageName, UserHandle.getUserId(proxyUid))
+                || !isIncomingPackageValid(proxiedPackageName, UserHandle.getUserId(proxiedUid))) {
+            return null;
+        }
 
         String resolvedProxyPackageName = AppOpsManager.resolvePackageName(proxyUid,
                 proxyPackageName);
@@ -4377,12 +4396,32 @@
         throw new IllegalArgumentException("Bad operation #" + op);
     }
 
-    private void verifyIncomingPackage(@Nullable String packageName, @UserIdInt int userId) {
-        if (packageName != null && getPackageManagerInternal().filterAppAccess(packageName,
-                Binder.getCallingUid(), userId)) {
-            throw new IllegalArgumentException(
-                    packageName + " not found from " + Binder.getCallingUid());
+    private boolean isIncomingPackageValid(@Nullable String packageName, @UserIdInt int userId) {
+        final int callingUid = Binder.getCallingUid();
+        // Handle the special UIDs that don't have actual packages (audioserver, cameraserver, etc).
+        if (packageName == null || isSpecialPackage(callingUid, packageName)) {
+            return true;
         }
+
+        // If the package doesn't exist, #verifyAndGetBypass would throw a SecurityException in
+        // the end. Although that exception would be caught and return, we could make it return
+        // early.
+        if (!isPackageExisted(packageName)) {
+            return false;
+        }
+
+        if (getPackageManagerInternal().filterAppAccess(packageName, callingUid, userId)) {
+            Slog.w(TAG, packageName + " not found from " + callingUid);
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean isSpecialPackage(int callingUid, @Nullable String packageName) {
+        final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid, packageName);
+        return callingUid == Process.SYSTEM_UID
+                || resolveUid(resolvedPackage) != Process.INVALID_UID;
     }
 
     private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) {
@@ -6672,7 +6711,9 @@
             }
         }
         verifyIncomingOp(code);
-        verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+        if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+            return false;
+        }
 
         final String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
@@ -7077,7 +7118,7 @@
 
     private static int resolveUid(String packageName)  {
         if (packageName == null) {
-            return -1;
+            return Process.INVALID_UID;
         }
         switch (packageName) {
             case "root":
@@ -7092,7 +7133,7 @@
             case "cameraserver":
                 return Process.CAMERASERVER_UID;
         }
-        return -1;
+        return Process.INVALID_UID;
     }
 
     private static String[] getPackagesForUid(int uid) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index aab6281..387e00f 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -20,9 +20,9 @@
 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
 import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.Process.SYSTEM_UID;
 
+import android.annotation.EnforcePermission;
+import android.annotation.RequiresNoPermission;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
@@ -93,15 +93,15 @@
     }
 
     @Override
+    @EnforcePermission(LOG_COMPAT_CHANGE)
     public void reportChange(long changeId, ApplicationInfo appInfo) {
-        checkCompatChangeLogPermission();
         reportChangeInternal(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED);
     }
 
     @Override
+    @EnforcePermission(LOG_COMPAT_CHANGE)
     public void reportChangeByPackageName(long changeId, String packageName,
             @UserIdInt int userId) {
-        checkCompatChangeLogPermission();
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo != null) {
             reportChangeInternal(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED);
@@ -109,8 +109,8 @@
     }
 
     @Override
+    @EnforcePermission(LOG_COMPAT_CHANGE)
     public void reportChangeByUid(long changeId, int uid) {
-        checkCompatChangeLogPermission();
         reportChangeInternal(changeId, uid, ChangeReporter.STATE_LOGGED);
     }
 
@@ -119,15 +119,15 @@
     }
 
     @Override
+    @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG})
     public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
-        checkCompatChangeReadAndLogPermission();
         return isChangeEnabledInternal(changeId, appInfo);
     }
 
     @Override
+    @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG})
     public boolean isChangeEnabledByPackageName(long changeId, String packageName,
             @UserIdInt int userId) {
-        checkCompatChangeReadAndLogPermission();
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo == null) {
             return mCompatConfig.willChangeBeEnabled(changeId, packageName);
@@ -136,8 +136,8 @@
     }
 
     @Override
+    @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG})
     public boolean isChangeEnabledByUid(long changeId, int uid) {
-        checkCompatChangeReadAndLogPermission();
         String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
         if (packages == null || packages.length == 0) {
             return mCompatConfig.defaultChangeIdValue(changeId);
@@ -197,8 +197,8 @@
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
     public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
-        checkCompatChangeOverridePermission();
         Map<Long, PackageOverride> overridesMap = new HashMap<>();
         for (long change : overrides.enabledChanges()) {
             overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build());
@@ -213,8 +213,8 @@
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
     public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
-        checkCompatChangeOverridePermission();
         Map<Long, PackageOverride> overridesMap = new HashMap<>();
         for (long change : overrides.enabledChanges()) {
             overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build());
@@ -228,9 +228,9 @@
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
     public void putAllOverridesOnReleaseBuilds(
             CompatibilityOverridesByPackageConfig overridesByPackage) {
-        checkCompatChangeOverrideOverridablePermission();
         for (CompatibilityOverrideConfig overrides :
                 overridesByPackage.packageNameToOverrides.values()) {
             checkAllCompatOverridesAreOverridable(overrides.overrides.keySet());
@@ -239,16 +239,16 @@
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
     public void putOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides,
             String packageName) {
-        checkCompatChangeOverrideOverridablePermission();
         checkAllCompatOverridesAreOverridable(overrides.overrides.keySet());
         mCompatConfig.addPackageOverrides(overrides, packageName, /* skipUnknownChangeIds= */ true);
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
     public int enableTargetSdkChanges(String packageName, int targetSdkVersion) {
-        checkCompatChangeOverridePermission();
         int numChanges =
                 mCompatConfig.enableTargetSdkChangesForPackage(packageName, targetSdkVersion);
         killPackage(packageName);
@@ -256,8 +256,8 @@
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
     public int disableTargetSdkChanges(String packageName, int targetSdkVersion) {
-        checkCompatChangeOverridePermission();
         int numChanges =
                 mCompatConfig.disableTargetSdkChangesForPackage(packageName, targetSdkVersion);
         killPackage(packageName);
@@ -265,36 +265,36 @@
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
     public void clearOverrides(String packageName) {
-        checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
         killPackage(packageName);
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
     public void clearOverridesForTest(String packageName) {
-        checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
     public boolean clearOverride(long changeId, String packageName) {
-        checkCompatChangeOverridePermission();
         boolean existed = mCompatConfig.removeOverride(changeId, packageName);
         killPackage(packageName);
         return existed;
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
     public boolean clearOverrideForTest(long changeId, String packageName) {
-        checkCompatChangeOverridePermission();
         return mCompatConfig.removeOverride(changeId, packageName);
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
     public void removeAllOverridesOnReleaseBuilds(
             CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage) {
-        checkCompatChangeOverrideOverridablePermission();
         for (CompatibilityOverridesToRemoveConfig overridesToRemove :
                 overridesToRemoveByPackage.packageNameToOverridesToRemove.values()) {
             checkAllCompatOverridesAreOverridable(overridesToRemove.changeIds);
@@ -303,27 +303,28 @@
     }
 
     @Override
+    @EnforcePermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
     public void removeOverridesOnReleaseBuilds(
             CompatibilityOverridesToRemoveConfig overridesToRemove,
             String packageName) {
-        checkCompatChangeOverrideOverridablePermission();
         checkAllCompatOverridesAreOverridable(overridesToRemove.changeIds);
         mCompatConfig.removePackageOverrides(overridesToRemove, packageName);
     }
 
     @Override
+    @EnforcePermission(allOf = {LOG_COMPAT_CHANGE, READ_COMPAT_CHANGE_CONFIG})
     public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
-        checkCompatChangeReadAndLogPermission();
         return mCompatConfig.getAppConfig(appInfo);
     }
 
     @Override
+    @EnforcePermission(READ_COMPAT_CHANGE_CONFIG)
     public CompatibilityChangeInfo[] listAllChanges() {
-        checkCompatChangeReadPermission();
         return mCompatConfig.dumpChanges();
     }
 
     @Override
+    @RequiresNoPermission
     public CompatibilityChangeInfo[] listUIChanges() {
         return Arrays.stream(listAllChanges()).filter(this::isShownInUI).toArray(
                 CompatibilityChangeInfo[]::new);
@@ -362,11 +363,15 @@
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) {
             return;
         }
-        checkCompatChangeReadAndLogPermission();
+        mContext.enforceCallingOrSelfPermission(
+                READ_COMPAT_CHANGE_CONFIG, "Cannot read compat change");
+        mContext.enforceCallingOrSelfPermission(
+                LOG_COMPAT_CHANGE, "Cannot read log compat change usage");
         mCompatConfig.dumpConfig(pw);
     }
 
     @Override
+    @RequiresNoPermission
     public IOverrideValidator getOverrideValidator() {
         return mCompatConfig.getOverrideValidator();
     }
@@ -414,49 +419,6 @@
         }
     }
 
-    private void checkCompatChangeLogPermission() throws SecurityException {
-        // Don't check for permissions within the system process
-        if (Binder.getCallingUid() == SYSTEM_UID) {
-            return;
-        }
-        if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) != PERMISSION_GRANTED) {
-            throw new SecurityException("Cannot log compat change usage");
-        }
-    }
-
-    private void checkCompatChangeReadPermission() {
-        // Don't check for permissions within the system process
-        if (Binder.getCallingUid() == SYSTEM_UID) {
-            return;
-        }
-        if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG)
-                != PERMISSION_GRANTED) {
-            throw new SecurityException("Cannot read compat change");
-        }
-    }
-
-    private void checkCompatChangeOverridePermission() {
-        // Don't check for permissions within the system process
-        if (Binder.getCallingUid() == SYSTEM_UID) {
-            return;
-        }
-        if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
-                != PERMISSION_GRANTED) {
-            throw new SecurityException("Cannot override compat change");
-        }
-    }
-
-    private void checkCompatChangeOverrideOverridablePermission() {
-        // Don't check for permissions within the system process
-        if (Binder.getCallingUid() == SYSTEM_UID) {
-            return;
-        }
-        if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
-                != PERMISSION_GRANTED) {
-            throw new SecurityException("Cannot override compat change");
-        }
-    }
-
     private void checkAllCompatOverridesAreOverridable(Collection<Long> changeIds) {
         for (Long changeId : changeIds) {
             if (isKnownChangeId(changeId) && !mCompatConfig.isOverridable(changeId)) {
@@ -466,11 +428,6 @@
         }
     }
 
-    private void checkCompatChangeReadAndLogPermission() {
-        checkCompatChangeReadPermission();
-        checkCompatChangeLogPermission();
-    }
-
     private boolean isShownInUI(CompatibilityChangeInfo change) {
         if (change.getLoggingOnly()) {
             return false;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index edaa18a..a661358 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -173,7 +173,7 @@
     public static final int TOUCH_VIRTUAL = 3;
 
     /**
-     * Diff result: The {@link #state} fields differ.
+     * Diff result: The {@link #state} or {@link #committedState} fields differ.
      */
     public static final int DIFF_STATE = 1 << 0;
 
@@ -335,6 +335,13 @@
     public int state = Display.STATE_ON;
 
     /**
+     * Display committed state.
+     *
+     * This matches {@link DisplayDeviceInfo#state} only after the power state change finishes.
+     */
+    public int committedState = Display.STATE_UNKNOWN;
+
+    /**
      * The UID of the application that owns this display, or zero if it is owned by the system.
      * <p>
      * If the display is private, then only the owner can use it.
@@ -387,7 +394,7 @@
      */
     public int diff(DisplayDeviceInfo other) {
         int diff = 0;
-        if (state != other.state) {
+        if (state != other.state || committedState != other.committedState) {
             diff |= DIFF_STATE;
         }
         if (colorMode != other.colorMode) {
@@ -461,6 +468,7 @@
         address = other.address;
         deviceProductInfo = other.deviceProductInfo;
         state = other.state;
+        committedState = other.committedState;
         ownerUid = other.ownerUid;
         ownerPackageName = other.ownerPackageName;
         frameRateOverrides = other.frameRateOverrides;
@@ -501,6 +509,7 @@
         }
         sb.append(", deviceProductInfo ").append(deviceProductInfo);
         sb.append(", state ").append(Display.stateToString(state));
+        sb.append(", committedState ").append(Display.stateToString(committedState));
         if (ownerUid != 0 || ownerPackageName != null) {
             sb.append(", owner ").append(ownerPackageName);
             sb.append(" (uid ").append(ownerUid).append(")");
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 0b9d4de..f98c7df 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -42,8 +42,8 @@
 import com.android.server.display.DisplayManagerService.Clock;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.Iterator;
-import java.util.LinkedList;
 
 /**
  * Controls the status of high-brightness mode for devices that support it. This class assumes that
@@ -110,11 +110,11 @@
     private long mRunningStartTimeMillis = -1;
 
     /**
-     * List of previous HBM-events ordered from most recent to least recent.
+     * Queue of previous HBM-events ordered from most recent to least recent.
      * Meant to store only the events that fall into the most recent
-     * {@link mHbmData.timeWindowMillis}.
+     * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
      */
-    private LinkedList<HbmEvent> mEvents = new LinkedList<>();
+    private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
 
     HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
             String displayUniqueId, float brightnessMin, float brightnessMax,
@@ -234,7 +234,7 @@
                 mRunningStartTimeMillis = -1;
 
                 if (DEBUG) {
-                    Slog.d(TAG, "New HBM event: " + mEvents.getFirst());
+                    Slog.d(TAG, "New HBM event: " + mEvents.peekFirst());
                 }
             }
         }
@@ -433,7 +433,7 @@
             // window by at least minTime. Basically, we're calculating the soonest time we can
             // get {@code timeMinMillis} back to us.
             final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
-            final HbmEvent lastEvent = mEvents.getLast();
+            final HbmEvent lastEvent = mEvents.peekLast();
             final long startTimePlusMinMillis =
                     Math.max(windowstartTimeMillis, lastEvent.startTimeMillis)
                     + mHbmData.timeMinMillis;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index a155095..84bce80 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -195,6 +195,8 @@
         private DisplayDeviceInfo mInfo;
         private boolean mHavePendingChanges;
         private int mState = Display.STATE_UNKNOWN;
+        private int mCommittedState = Display.STATE_UNKNOWN;
+
         // This is only set in the runnable returned from requestDisplayStateLocked.
         private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
@@ -635,6 +637,7 @@
                 mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos;
                 mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos;
                 mInfo.state = mState;
+                mInfo.committedState = mCommittedState;
                 mInfo.uniqueId = getUniqueId();
                 final DisplayAddress.Physical physicalAddress =
                         DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
@@ -815,6 +818,7 @@
                         } finally {
                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
                         }
+                        setCommittedState(state);
                         // If we're entering a suspended (but not OFF) power state and we
                         // have a sidekick available, tell it now that it can take control.
                         if (Display.isSuspendedState(state) && state != Display.STATE_OFF
@@ -829,6 +833,16 @@
                         }
                     }
 
+                    private void setCommittedState(int state) {
+                        // After the display state is set, let's update the committed state.
+                        getHandler().post(() -> {
+                            synchronized (getSyncRoot()) {
+                                mCommittedState = state;
+                                updateDeviceInfoLocked();
+                            }
+                        });
+                    }
+
                     private void setDisplayBrightness(float brightnessState,
                             float sdrBrightnessState) {
                         // brightnessState includes invalid, off and full range.
@@ -1094,6 +1108,7 @@
             pw.println("mDefaultModeId=" + mDefaultModeId);
             pw.println("mUserPreferredModeId=" + mUserPreferredModeId);
             pw.println("mState=" + Display.stateToString(mState));
+            pw.println("mCommittedState=" + Display.stateToString(mCommittedState));
             pw.println("mBrightnessState=" + mBrightnessState);
             pw.println("mBacklightAdapter=" + mBacklightAdapter);
             pw.println("mAllmSupported=" + mAllmSupported);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index a640497..839555b 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -413,6 +413,7 @@
             mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;
             mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;
             mBaseDisplayInfo.state = deviceInfo.state;
+            mBaseDisplayInfo.committedState = deviceInfo.committedState;
             mBaseDisplayInfo.smallestNominalAppWidth = maskedWidth;
             mBaseDisplayInfo.smallestNominalAppHeight = maskedHeight;
             mBaseDisplayInfo.largestNominalAppWidth = maskedWidth;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 576a5ff..26e38bd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -306,16 +306,23 @@
     @Override
     @ServiceThreadOnly
     protected void onInitializeCecComplete(int initiatedBy) {
-        if (initiatedBy == HdmiControlService.INITIATED_BY_SCREEN_ON) {
-            oneTouchPlay(new IHdmiControlCallback.Stub() {
-                @Override
-                public void onComplete(int result) {
-                    if (result != HdmiControlManager.RESULT_SUCCESS) {
-                        Slog.w(TAG, "Failed to complete One Touch Play. result=" + result);
-                    }
-                }
-            });
+        if (initiatedBy != HdmiControlService.INITIATED_BY_SCREEN_ON) {
+            return;
         }
+        @HdmiControlManager.PowerControlMode
+        String powerControlMode = mService.getHdmiCecConfig().getStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+        if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_NONE)) {
+            return;
+        }
+        oneTouchPlay(new IHdmiControlCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+                if (result != HdmiControlManager.RESULT_SUCCESS) {
+                    Slog.w(TAG, "Failed to complete One Touch Play. result=" + result);
+                }
+            }
+        });
     }
 
     @Override
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 8ab0b93..e433324 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.input;
 
+import static android.provider.DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT;
 import static android.view.KeyEvent.KEYCODE_UNKNOWN;
 
 import android.annotation.NonNull;
@@ -157,6 +158,8 @@
 
     // Feature flag name for the deep press feature
     private static final String DEEP_PRESS_ENABLED = "deep_press_enabled";
+    // Feature flag name for the strategy to be used in VelocityTracker
+    private static final String VELOCITYTRACKER_STRATEGY_PROPERTY = "velocitytracker_strategy";
 
     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
     private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
@@ -358,6 +361,8 @@
     public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
     public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
 
+    private final String mVelocityTrackerStrategy;
+
     /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
     final boolean mUseDevInputEventForAudioJack;
 
@@ -406,6 +411,8 @@
         mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
             new File(doubleTouchGestureEnablePath);
 
+        mVelocityTrackerStrategy = DeviceConfig.getProperty(
+                NAMESPACE_INPUT_NATIVE_BOOT, VELOCITYTRACKER_STRATEGY_PROPERTY);
         LocalServices.addService(InputManagerInternal.class, new LocalService());
     }
 
@@ -878,6 +885,11 @@
         return mNative.verifyInputEvent(event);
     }
 
+    @Override // Binder call
+    public String getVelocityTrackerStrategy() {
+        return mVelocityTrackerStrategy;
+    }
+
     /**
      * Gets information about the input device with the specified id.
      * @param deviceId The device id.
@@ -2343,7 +2355,7 @@
     public void removePortAssociation(@NonNull String inputPort) {
         if (!checkCallingPermission(
                 android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
-                "clearPortAssociations()")) {
+                "removePortAssociation()")) {
             throw new SecurityException(
                     "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
         }
@@ -2359,7 +2371,7 @@
     public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
         if (!checkCallingPermission(
                 android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
-                "addNameAssociation()")) {
+                "addUniqueIdAssociation()")) {
             throw new SecurityException(
                     "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3c31405..8cf0ab74 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4734,7 +4734,8 @@
     }
 
     @BinderThread
-    private void hideMySoftInput(@NonNull IBinder token, int flags) {
+    private void hideMySoftInput(@NonNull IBinder token, int flags,
+            @SoftInputShowHideReason int reason) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
         synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
@@ -4742,10 +4743,7 @@
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                hideCurrentInputLocked(
-                        mLastImeTargetWindow, flags, null,
-                        SoftInputShowHideReason.HIDE_MY_SOFT_INPUT);
-
+                hideCurrentInputLocked(mLastImeTargetWindow, flags, null, reason);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4763,7 +4761,7 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 showCurrentInputLocked(mLastImeTargetWindow, flags, null,
-                        SoftInputShowHideReason.SHOW_MY_SOFT_INPUT);
+                        SoftInputShowHideReason.SHOW_SOFT_INPUT_FROM_IME);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -6571,11 +6569,12 @@
 
         @BinderThread
         @Override
-        public void hideMySoftInput(int flags, AndroidFuture future /* T=Void */) {
+        public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason,
+                AndroidFuture future /* T=Void */) {
             @SuppressWarnings("unchecked")
             final AndroidFuture<Void> typedFuture = future;
             try {
-                mImms.hideMySoftInput(mToken, flags);
+                mImms.hideMySoftInput(mToken, flags, reason);
                 typedFuture.complete(null);
             } catch (Throwable e) {
                 typedFuture.completeExceptionally(e);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 42fed36..9e5da45 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1179,8 +1179,15 @@
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
-            (new MediaShellCommand()).exec(this, in, out, err, args, callback,
-                    resultReceiver);
+            String[] packageNames =
+                    mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
+            String packageName = packageNames != null && packageNames.length > 0
+                    ? packageNames[0]
+                    : "com.android.shell"; // We should not need this branch, but defaulting to the
+                                           // current shell package name for robustness. See
+                                           // b/227109905.
+            new MediaShellCommand(packageName)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index 103cdd9..d175d87 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -47,15 +47,19 @@
  * ShellCommand for MediaSessionService.
  */
 public class MediaShellCommand extends ShellCommand {
-    // This doesn't belongs to any package. Setting the package name to empty string.
-    private static final String PACKAGE_NAME = "";
     private static ActivityThread sThread;
     private static MediaSessionManager sMediaSessionManager;
+
+    private final String mPackageName;
     private ISessionManager mSessionService;
     private PrintWriter mWriter;
     private PrintWriter mErrorWriter;
     private InputStream mInput;
 
+    public MediaShellCommand(String packageName) {
+        mPackageName = packageName;
+    }
+
     @Override
     public int onCommand(String cmd) {
         mWriter = getOutPrintWriter();
@@ -110,7 +114,7 @@
         mWriter.println();
         mWriter.println("media_session dispatch: dispatch a media key to the system.");
         mWriter.println("                KEY may be: play, pause, play-pause, mute, headsethook,");
-        mWriter.println("                stop, next, previous, rewind, record, fast-forword.");
+        mWriter.println("                stop, next, previous, rewind, record, fast-forward.");
         mWriter.println("media_session list-sessions: print a list of the current sessions.");
         mWriter.println("media_session monitor: monitor updates to the specified session.");
         mWriter.println("                       Use the tag from list-sessions.");
@@ -120,7 +124,8 @@
 
     private void sendMediaKey(KeyEvent event) {
         try {
-            mSessionService.dispatchMediaKeyEvent(PACKAGE_NAME, false, event, false);
+            mSessionService.dispatchMediaKeyEvent(
+                    mPackageName, /* asSystemService= */ false, event, /* needWakeLock= */ false);
         } catch (RemoteException e) {
         }
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index e5a6e65..c7c71af 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -28,6 +28,7 @@
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
 import static android.app.ActivityManager.isProcStateConsideredInteraction;
+import static android.app.ActivityManager.procStateToString;
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
@@ -1119,7 +1120,9 @@
                     callbackInfo = new UidStateCallbackInfo();
                     mUidStateCallbackInfos.put(uid, callbackInfo);
                 }
-                callbackInfo.update(uid, procState, procStateSeq, capability);
+                if (callbackInfo.procStateSeq == -1 || procStateSeq > callbackInfo.procStateSeq) {
+                    callbackInfo.update(uid, procState, procStateSeq, capability);
+                }
                 if (!callbackInfo.isPending) {
                     mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo)
                             .sendToTarget();
@@ -1146,8 +1149,8 @@
 
     private static final class UidStateCallbackInfo {
         public int uid;
-        public int procState;
-        public long procStateSeq;
+        public int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+        public long procStateSeq = -1;
         @ProcessCapability
         public int capability;
         public boolean isPending;
@@ -4044,13 +4047,22 @@
      * {@link #updateRulesForPowerRestrictionsUL(int)}. Returns true if the state was updated.
      */
     @GuardedBy("mUidRulesFirstLock")
-    private boolean updateUidStateUL(int uid, int procState, @ProcessCapability int capability) {
+    private boolean updateUidStateUL(int uid, int procState, long procStateSeq,
+            @ProcessCapability int capability) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
         try {
             final UidState oldUidState = mUidState.get(uid);
+            if (oldUidState != null && procStateSeq < oldUidState.procStateSeq) {
+                if (LOGV) {
+                    Slog.v(TAG, "Ignoring older uid state updates; uid=" + uid
+                            + ",procState=" + procStateToString(procState) + ",seq=" + procStateSeq
+                            + ",cap=" + capability + ",oldUidState=" + oldUidState);
+                }
+                return false;
+            }
             if (oldUidState == null || oldUidState.procState != procState
                     || oldUidState.capability != capability) {
-                final UidState newUidState = new UidState(uid, procState, capability);
+                final UidState newUidState = new UidState(uid, procState, procStateSeq, capability);
                 // state changed, push updated rules
                 mUidState.put(uid, newUidState);
                 updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState);
@@ -4797,6 +4809,7 @@
         synchronized (mUidBlockedState) {
             mUidBlockedState.delete(uid);
         }
+        mUidState.delete(uid);
         mUidPolicy.delete(uid);
         mUidFirewallStandbyRules.delete(uid);
         mUidFirewallDozableRules.delete(uid);
@@ -5438,21 +5451,7 @@
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
                 case UID_MSG_STATE_CHANGED: {
-                    final UidStateCallbackInfo uidStateCallbackInfo =
-                            (UidStateCallbackInfo) msg.obj;
-                    final int uid;
-                    final int procState;
-                    final long procStateSeq;
-                    final int capability;
-                    synchronized (mUidStateCallbackInfos) {
-                        uid = uidStateCallbackInfo.uid;
-                        procState = uidStateCallbackInfo.procState;
-                        procStateSeq = uidStateCallbackInfo.procStateSeq;
-                        capability = uidStateCallbackInfo.capability;
-                        uidStateCallbackInfo.isPending = false;
-                    }
-
-                    handleUidChanged(uid, procState, procStateSeq, capability);
+                    handleUidChanged((UidStateCallbackInfo) msg.obj);
                     return true;
                 }
                 case UID_MSG_GONE: {
@@ -5467,17 +5466,28 @@
         }
     };
 
-    void handleUidChanged(int uid, int procState, long procStateSeq,
-            @ProcessCapability int capability) {
+    void handleUidChanged(@NonNull UidStateCallbackInfo uidStateCallbackInfo) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged");
         try {
             boolean updated;
+            final int uid;
+            final int procState;
+            final long procStateSeq;
+            final int capability;
             synchronized (mUidRulesFirstLock) {
+                synchronized (mUidStateCallbackInfos) {
+                    uid = uidStateCallbackInfo.uid;
+                    procState = uidStateCallbackInfo.procState;
+                    procStateSeq = uidStateCallbackInfo.procStateSeq;
+                    capability = uidStateCallbackInfo.capability;
+                    uidStateCallbackInfo.isPending = false;
+                }
+
                 // We received a uid state change callback, add it to the history so that it
                 // will be useful for debugging.
                 mLogger.uidStateChanged(uid, procState, procStateSeq, capability);
                 // Now update the network policy rules as per the updated uid state.
-                updated = updateUidStateUL(uid, procState, capability);
+                updated = updateUidStateUL(uid, procState, procStateSeq, capability);
                 // Updating the network rules is done, so notify AMS about this.
                 mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq);
             }
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index a9b2570..e09f7b0 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -40,11 +40,12 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.Iterator;
-import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -76,7 +77,7 @@
     private final Handler mFileWriteHandler;
     @VisibleForTesting
     // List of files holding history information, sorted newest to oldest
-    final LinkedList<AtomicFile> mHistoryFiles;
+    final List<AtomicFile> mHistoryFiles;
     private final File mHistoryDir;
     private final File mVersionFile;
     // Current version of the database files schema
@@ -94,7 +95,7 @@
         mFileWriteHandler = fileWriteHandler;
         mVersionFile = new File(dir, "version");
         mHistoryDir = new File(dir, "history");
-        mHistoryFiles = new LinkedList<>();
+        mHistoryFiles = new ArrayList<>();
         mBuffer = new NotificationHistory();
         mWriteBufferRunnable = new WriteBufferRunnable();
 
@@ -133,7 +134,7 @@
                 safeParseLong(lhs.getName())));
 
         for (File file : files) {
-            mHistoryFiles.addLast(new AtomicFile(file));
+            mHistoryFiles.add(new AtomicFile(file));
         }
     }
 
@@ -411,7 +412,7 @@
                         + file.getBaseFile().getAbsolutePath());
                 try {
                     writeLocked(file, mBuffer);
-                    mHistoryFiles.addFirst(file);
+                    mHistoryFiles.add(0, file);
                     mBuffer = new NotificationHistory();
 
                     scheduleDeletion(file.getBaseFile(), time, HISTORY_RETENTION_DAYS);
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 903d02a..f3cb7fb 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -19,6 +19,7 @@
 import static com.android.server.om.OverlayManagerService.DEBUG;
 import static com.android.server.om.OverlayManagerService.TAG;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.om.OverlayInfo;
 import android.content.om.OverlayableInfo;
@@ -33,6 +34,8 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -56,6 +59,18 @@
         VENDOR_IS_Q_OR_LATER = isQOrLater;
     }
 
+    static final int IDMAP_NOT_EXIST = 0;
+    static final int IDMAP_IS_VERIFIED = 1;
+    static final int IDMAP_IS_MODIFIED = 1 << 1;
+
+    @IntDef(flag = true, prefix = { "IDMAP_" }, value = {
+            IDMAP_NOT_EXIST,
+            IDMAP_IS_VERIFIED,
+            IDMAP_IS_MODIFIED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IdmapStatus {}
+
     private final IdmapDaemon mIdmapDaemon;
     private final PackageManagerHelper mPackageManager;
 
@@ -76,8 +91,14 @@
     /**
      * Creates the idmap for the target/overlay combination and returns whether the idmap file was
      * modified.
+     * @return the status of the specific idmap file. It's one of the following.<ul>
+     *     <li>{@link #IDMAP_NOT_EXIST} means the idmap file is not existed.</li>
+     *     <li>{@link #IDMAP_IS_VERIFIED} means the idmap file is verified by Idmap2d.</li>
+     *     <li>{@link #IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED } means the idmap file is modified and
+     *     verified by Idmap2d.</li>
+     * </ul>.
      */
-    boolean createIdmap(@NonNull final AndroidPackage targetPackage,
+    @IdmapStatus int createIdmap(@NonNull final AndroidPackage targetPackage,
             @NonNull final AndroidPackage overlayPackage, String overlayBasePath,
             String overlayName, int userId) {
         if (DEBUG) {
@@ -90,14 +111,15 @@
             boolean enforce = enforceOverlayable(overlayPackage);
             if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies,
                     enforce, userId)) {
-                return false;
+                return IDMAP_IS_VERIFIED;
             }
-            return mIdmapDaemon.createIdmap(targetPath, overlayBasePath, overlayName, policies,
-                    enforce, userId) != null;
+            final boolean idmapCreated = mIdmapDaemon.createIdmap(targetPath, overlayBasePath,
+                    overlayName, policies, enforce, userId) != null;
+            return (idmapCreated) ? IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED : IDMAP_NOT_EXIST;
         } catch (Exception e) {
             Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
                     + overlayBasePath, e);
-            return false;
+            return IDMAP_NOT_EXIST;
         }
     }
 
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8ecc607..9ba1552 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1477,7 +1477,7 @@
                 targetPackageNames = pm.getTargetPackageNames(userId);
             }
 
-            final Map<String, OverlayPaths> pendingChanges =
+            final ArrayMap<String, OverlayPaths> pendingChanges =
                     new ArrayMap<>(targetPackageNames.size());
             synchronized (mLock) {
                 final OverlayPaths frameworkOverlays =
@@ -1493,6 +1493,9 @@
             }
 
             final HashSet<String> updatedPackages = new HashSet<>();
+            final HashSet<String> invalidPackages = new HashSet<>();
+            pm.setEnabledOverlayPackages(userId, pendingChanges, updatedPackages, invalidPackages);
+
             for (final String targetPackageName : targetPackageNames) {
                 if (DEBUG) {
                     Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
@@ -1500,11 +1503,10 @@
                             + "] userId=" + userId);
                 }
 
-                if (!pm.setEnabledOverlayPackages(
-                        userId, targetPackageName, pendingChanges.get(targetPackageName),
-                        updatedPackages)) {
-                    Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
-                            targetPackageName, userId));
+                if (invalidPackages.contains(targetPackageName)) {
+                    Slog.e(TAG, TextUtils.formatSimple(
+                            "Failed to change enabled overlays for %s user %d", targetPackageName,
+                            userId));
                 }
             }
             return new ArrayList<>(updatedPackages);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 38781fa..e15e65a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -24,6 +24,9 @@
 import static android.content.om.OverlayInfo.STATE_TARGET_IS_BEING_REPLACED;
 import static android.os.UserHandle.USER_SYSTEM;
 
+import static com.android.server.om.IdmapManager.IDMAP_IS_MODIFIED;
+import static com.android.server.om.IdmapManager.IDMAP_IS_VERIFIED;
+import static com.android.server.om.IdmapManager.IDMAP_NOT_EXIST;
 import static com.android.server.om.OverlayManagerService.DEBUG;
 import static com.android.server.om.OverlayManagerService.TAG;
 
@@ -785,15 +788,18 @@
         // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native
         // layers.
         final OverlayInfo updatedOverlayInfo = mSettings.getOverlayInfo(overlay, userId);
+        @IdmapManager.IdmapStatus int idmapStatus = IDMAP_NOT_EXIST;
         if (targetPackage != null && !("android".equals(info.getTargetPackageName())
                 && !isPackageConfiguredMutable(overlayPackage))) {
-            modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage,
-                    updatedOverlayInfo.baseCodePath, overlay.getOverlayName(), userId);
+            idmapStatus = mIdmapManager.createIdmap(targetPackage,
+                    overlayPackage, updatedOverlayInfo.baseCodePath, overlay.getOverlayName(),
+                    userId);
+            modified |= (idmapStatus & IDMAP_IS_MODIFIED) != 0;
         }
 
         final @OverlayInfo.State int currentState = mSettings.getState(overlay, userId);
         final @OverlayInfo.State int newState = calculateNewState(updatedOverlayInfo, targetPackage,
-                userId, flags);
+                userId, flags, idmapStatus);
         if (currentState != newState) {
             if (DEBUG) {
                 Slog.d(TAG, String.format("%s:%d: %s -> %s",
@@ -808,7 +814,8 @@
     }
 
     private @OverlayInfo.State int calculateNewState(@NonNull final OverlayInfo info,
-            @Nullable final AndroidPackage targetPackage, final int userId, final int flags)
+            @Nullable final AndroidPackage targetPackage, final int userId, final int flags,
+            @IdmapManager.IdmapStatus final int idmapStatus)
             throws OverlayManagerSettings.BadKeyException {
         if ((flags & FLAG_TARGET_IS_BEING_REPLACED) != 0) {
             return STATE_TARGET_IS_BEING_REPLACED;
@@ -822,8 +829,10 @@
             return STATE_MISSING_TARGET;
         }
 
-        if (!mIdmapManager.idmapExists(info)) {
-            return STATE_NO_IDMAP;
+        if ((idmapStatus & IDMAP_IS_VERIFIED) == 0) {
+            if (!mIdmapManager.idmapExists(info)) {
+                return STATE_NO_IDMAP;
+            }
         }
 
         final boolean enabled = mSettings.getEnabled(info.getOverlayIdentifier(), userId);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a91c55f..e07e3c1 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -119,16 +119,18 @@
         @Nullable public final String apexModuleName;
         public final File apexDirectory;
         public final File preInstalledApexPath;
+        public final boolean isFactory;
 
         private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) {
-            this(null, apexDirectory, preInstalledApexPath);
+            this(null, apexDirectory, preInstalledApexPath, true);
         }
 
         private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory,
-                File preInstalledApexPath) {
+                File preInstalledApexPath, boolean isFactory) {
             this.apexModuleName = apexModuleName;
             this.apexDirectory = apexDirectory;
             this.preInstalledApexPath = preInstalledApexPath;
+            this.isFactory = isFactory;
         }
 
         private ActiveApexInfo(ApexInfo apexInfo) {
@@ -136,7 +138,8 @@
                     apexInfo.moduleName,
                     new File(Environment.getApexDirectory() + File.separator
                             + apexInfo.moduleName),
-                    new File(apexInfo.preinstalledModulePath));
+                    new File(apexInfo.preinstalledModulePath),
+                    apexInfo.isFactory);
         }
     }
 
@@ -211,7 +214,7 @@
      * @return {@code true} if this package is pre-installed, {@code false} otherwise.
      */
     public static boolean isFactory(@NonNull PackageInfo packageInfo) {
-        return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+        return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0;
     }
 
     /**
@@ -503,7 +506,7 @@
          * @return {@code true} if this package is active, {@code false} otherwise.
          */
         private static boolean isActive(PackageInfo packageInfo) {
-            return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
+            return packageInfo.isActiveApex;
         }
 
         /**
@@ -1150,7 +1153,7 @@
                 // Installation was successful, time to update mAllPackagesCache
                 synchronized (mLock) {
                     if (isFactory(existingApexPkg)) {
-                        existingApexPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+                        existingApexPkg.isActiveApex = false;
                         mAllPackagesCache.add(finalApexPkg);
                     } else {
                         for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 80d61b5..a548b72 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -540,8 +540,13 @@
                                 && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
                                 || (matchVisibleToInstantAppOnly && isCallerInstantApp
                                 && isTargetHiddenFromInstantApp));
+                final boolean resolveForStartNonExported = resolveForStart
+                                && !ai.exported
+                                && !isCallerSameApp(pkgName, filterCallingUid);
                 final boolean blockNormalResolution =
-                        !resolveForStart && !isTargetInstantApp && !isCallerInstantApp
+                        (!resolveForStart || resolveForStartNonExported)
+                                && !isTargetInstantApp
+                                && !isCallerInstantApp
                                 && shouldFilterApplication(
                                 getPackageStateInternal(ai.applicationInfo.packageName,
                                         Process.SYSTEM_UID), filterCallingUid, userId);
@@ -1835,9 +1840,6 @@
                 list.addAll(mApexManager.getFactoryPackages());
             } else {
                 list.addAll(mApexManager.getActivePackages());
-                if (listUninstalled) {
-                    list.addAll(mApexManager.getInactivePackages());
-                }
             }
         }
         return new ParceledListSlice<>(list);
@@ -4345,11 +4347,8 @@
 
     @Override
     public List<String> getAllPackages() {
-        // Allow iorapd to call this method.
-        if (Binder.getCallingUid() != Process.IORAPD_UID) {
-            PackageManagerServiceUtils.enforceSystemOrRootOrShell(
-                    "getAllPackages is limited to privileged callers");
-        }
+        PackageManagerServiceUtils.enforceSystemOrRootOrShell(
+                "getAllPackages is limited to privileged callers");
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
         if (canViewInstantApps(callingUid, callingUserId)) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 7dae22a..52d4873 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -39,7 +39,6 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDeleteObserver2;
-import android.content.pm.PackageChangeEvent;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.SharedLibraryInfo;
@@ -762,7 +761,6 @@
             } catch (RemoteException e) {
                 Log.i(TAG, "Observer no longer exists.");
             } //end catch
-            notifyPackageChangeObserversOnDelete(packageName, versionCode);
 
             // Prune unused static shared libraries which have been cached a period of time
             mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */);
@@ -822,18 +820,6 @@
         return result;
     }
 
-    private void notifyPackageChangeObserversOnDelete(String packageName, long version) {
-        PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
-        pkgChangeEvent.packageName = packageName;
-        pkgChangeEvent.version = version;
-        pkgChangeEvent.lastUpdateTimeMillis = 0L;
-        pkgChangeEvent.newInstalled = false;
-        pkgChangeEvent.dataRemoved = false;
-        pkgChangeEvent.isDeleted = true;
-
-        mPm.notifyPackageChangeObservers(pkgChangeEvent);
-    }
-
     private static class TempUserState {
         public final int enabledState;
         @Nullable
diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
index 7dc45b5..b28b73e 100644
--- a/services/core/java/com/android/server/pm/DistractingPackageHelper.java
+++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
+import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.NonNull;
 import android.content.Intent;
@@ -27,11 +28,13 @@
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.pkg.PackageStateInternal;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -126,7 +129,7 @@
         if (!changedPackagesList.isEmpty()) {
             final String[] changedPackages = changedPackagesList.toArray(
                     new String[changedPackagesList.size()]);
-            sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
+            sendDistractingPackagesChanged(snapshot, changedPackages, changedUids.toArray(), userId,
                     restrictionFlags);
             mPm.scheduleWritePackageRestrictions(userId);
         }
@@ -168,7 +171,7 @@
         if (!changedPackages.isEmpty()) {
             final String[] packageArray = changedPackages.toArray(
                     new String[changedPackages.size()]);
-            sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId,
+            sendDistractingPackagesChanged(snapshot, packageArray, changedUids.toArray(), userId,
                     RESTRICTION_NONE);
             mPm.scheduleWritePackageRestrictions(userId);
         }
@@ -181,18 +184,53 @@
      * @param uidList The uids of packages which have suspension changes.
      * @param userId The user where packages reside.
      */
-    void sendDistractingPackagesChanged(@NonNull String[] pkgList,
+    void sendDistractingPackagesChanged(@NonNull Computer snapshot, @NonNull String[] pkgList,
             int[] uidList, int userId, int distractionFlags) {
-        final Bundle extras = new Bundle(3);
-        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
-        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
-        extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+        final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
+        final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
+        final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
+        final int[] userIds = new int[] {userId};
+        // Get allow lists for the pkg in the pkgList. Merge into the existed pkgs and uids if
+        // allow lists are the same.
+        for (int i = 0; i < pkgList.length; i++) {
+            final String pkgName = pkgList[i];
+            final int uid = uidList[i];
+            SparseArray<int[]> allowList = mInjector.getAppsFilter().getVisibilityAllowList(
+                    snapshot.getPackageStateInternal(pkgName, SYSTEM_UID),
+                    userIds, snapshot.getPackageStates());
+            if (allowList == null) {
+                allowList = new SparseArray<>(0);
+            }
+            boolean merged = false;
+            for (int j = 0; j < allowListsToSend.size(); j++) {
+                if (Arrays.equals(allowListsToSend.get(j).get(userId), allowList.get(userId))) {
+                    pkgsToSend.get(j).add(pkgName);
+                    uidsToSend.get(j).add(uid);
+                    merged = true;
+                    break;
+                }
+            }
+            if (!merged) {
+                pkgsToSend.add(new ArrayList<>(Arrays.asList(pkgName)));
+                uidsToSend.add(IntArray.wrap(new int[] {uid}));
+                allowListsToSend.add(allowList);
+            }
+        }
 
         final Handler handler = mInjector.getHandler();
-        handler.post(() -> mBroadcastHelper.sendPackageBroadcast(
-                Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */, extras,
-                Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
-                null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
-                null /* allowList */, null /* bOptions */));
+        for (int i = 0; i < pkgsToSend.size(); i++) {
+            final Bundle extras = new Bundle(3);
+            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+                    pkgsToSend.get(i).toArray(new String[pkgsToSend.get(i).size()]));
+            extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
+            extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+            final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
+                    ? null : allowListsToSend.get(i);
+            handler.post(() -> mBroadcastHelper.sendPackageBroadcast(
+                    Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */,
+                    extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
+                    null /* finishedReceiver */, userIds, null /* instantUserIds */,
+                    allowList, null /* bOptions */));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 154f32a..3dd0022 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -21,6 +21,7 @@
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
 import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
@@ -31,6 +32,7 @@
 import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
 import static com.android.server.pm.PackageManagerService.TAG;
 import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
+import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -167,7 +169,11 @@
                     sp.getFolder().getAbsolutePath())
                     || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
                     sp.getFolder().getAbsolutePath() + File.separator)) {
-                return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
+                int additionalScanFlag = SCAN_AS_APK_IN_APEX;
+                if (apexInfo.isFactory) {
+                    additionalScanFlag |= SCAN_AS_FACTORY;
+                }
+                return new ScanPartition(apexInfo.apexDirectory, sp, additionalScanFlag);
             }
         }
         return null;
@@ -317,8 +323,9 @@
                     packageParser, executorService);
         }
 
-        scanDirTracedLI(frameworkDir, null,
-                mSystemParseFlags,
+        List<File> frameworkSplits = getFrameworkResApkSplitFiles();
+        scanDirTracedLI(frameworkDir, frameworkSplits,
+                mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS,
                 mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
                 packageParser, executorService);
         if (!mPm.mPackages.containsKey("android")) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 77d37dc..3b75947 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -60,6 +60,7 @@
 import static com.android.server.pm.PackageManagerService.POST_INSTALL;
 import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
@@ -101,7 +102,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.DataLoaderType;
 import android.content.pm.IPackageInstallObserver2;
-import android.content.pm.PackageChangeEvent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
@@ -315,6 +315,12 @@
             pkgSetting.setInstallSource(installSource);
         }
 
+        if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
+            boolean isFactory = (scanFlags & SCAN_AS_FACTORY) != 0;
+            pkgSetting.getPkgState().setApkInApex(true);
+            pkgSetting.getPkgState().setApkInUpdatedApex(!isFactory);
+        }
+
         // TODO(toddke): Consider a method specifically for modifying the Package object
         // post scan; or, moving this stuff out of the Package object since it has nothing
         // to do with the package on disk.
@@ -2341,29 +2347,11 @@
             // BackgroundDexOptService will remove it from its denylist.
             // TODO: Layering violation
             BackgroundDexOptService.getService().notifyPackageChanged(packageName);
-
-            notifyPackageChangeObserversOnUpdate(reconciledPkg);
         }
         PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
                 incrementalStorages);
     }
 
-    private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
-        final PackageSetting pkgSetting = reconciledPkg.mPkgSetting;
-        final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.mInstallResult;
-        final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.mRemovedInfo;
-
-        PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
-        pkgChangeEvent.packageName = pkgSetting.getPkg().getPackageName();
-        pkgChangeEvent.version = pkgSetting.getVersionCode();
-        pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.getLastUpdateTime();
-        pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate);
-        pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved);
-        pkgChangeEvent.isDeleted = false;
-
-        mPm.notifyPackageChangeObservers(pkgChangeEvent);
-    }
-
     public int installLocationPolicy(PackageInfoLite pkgLite, int installFlags) {
         String packageName = pkgLite.packageName;
         int installLocation = pkgLite.installLocation;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7c900ef..3e314a4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -299,6 +299,10 @@
     final SessionParams params;
     final long createdMillis;
 
+    /** Used for tracking whether user action was required for an install. */
+    @Nullable
+    private Boolean mUserActionRequired;
+
     /** Staging location where client data is written. */
     final File stageDir;
     final String stageCid;
@@ -2131,8 +2135,16 @@
          * in its belong session set. When the user answers the yes,
          * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
          * handled to come back here to check again.
+         *
+         * {@code mUserActionRequired} is used to track when user action is required for an
+         * install. Since control may come back here more than 1 time, we must ensure that it's
+         * value is not overwritten.
          */
-        if (sendPendingUserActionIntentIfNeeded()) {
+        boolean wasUserActionIntentSent = sendPendingUserActionIntentIfNeeded();
+        if (mUserActionRequired == null) {
+            mUserActionRequired = wasUserActionIntentSent;
+        }
+        if (wasUserActionIntentSent) {
             return;
         }
 
@@ -3323,6 +3335,18 @@
         }
     }
 
+    /**
+     * @return a boolean value indicating whether user action was requested for the install.
+     * Returns {@code false} if {@code mUserActionRequired} is {@code null}
+     */
+    public boolean getUserActionRequired() {
+        if (mUserActionRequired != null) {
+            return mUserActionRequired.booleanValue();
+        }
+        Slog.wtf(TAG, "mUserActionRequired should not be null.");
+        return false;
+    }
+
     private static String getRelativePath(File file, File base) throws IOException {
         final String pathStr = file.getAbsolutePath();
         final String baseStr = base.getAbsolutePath();
diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java
index 9a43008..77d2ec9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerNative.java
+++ b/services/core/java/com/android/server/pm/PackageManagerNative.java
@@ -20,20 +20,16 @@
 
 import static com.android.server.pm.PackageManagerService.TAG;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageManagerNative;
 import android.content.pm.IStagedApexObserver;
 import android.content.pm.PackageInfo;
 import android.content.pm.StagedApexInfo;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
-import android.util.Log;
 import android.util.Slog;
 
 import java.util.Arrays;
@@ -46,35 +42,6 @@
     }
 
     @Override
-    public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
-        synchronized (mPm.mPackageChangeObservers) {
-            try {
-                observer.asBinder().linkToDeath(
-                        new PackageChangeObserverDeathRecipient(observer), 0);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.getMessage());
-            }
-            mPm.mPackageChangeObservers.add(observer);
-            Log.d(TAG, "Size of mPackageChangeObservers after registry is "
-                    + mPm.mPackageChangeObservers.size());
-        }
-    }
-
-    @Override
-    public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
-        synchronized (mPm.mPackageChangeObservers) {
-            mPm.mPackageChangeObservers.remove(observer);
-            Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
-                    + mPm.mPackageChangeObservers.size());
-        }
-    }
-
-    @Override
-    public String[] getAllPackages() {
-        return mPm.snapshotComputer().getAllPackages().toArray(new String[0]);
-    }
-
-    @Override
     public String[] getNamesForUids(int[] uids) throws RemoteException {
         String[] names = null;
         String[] results = null;
@@ -222,21 +189,4 @@
     public StagedApexInfo getStagedApexInfo(String moduleName) {
         return mPm.mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
     }
-
-    private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
-        private final IPackageChangeObserver mObserver;
-
-        PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
-            mObserver = observer;
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mPm.mPackageChangeObservers) {
-                mPm.mPackageChangeObservers.remove(mObserver);
-                Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
-                        + mPm.mPackageChangeObservers.size());
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e1af9a7..8e13017 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -80,7 +80,6 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
 import android.content.pm.IOnChecksumsReadyListener;
-import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver2;
 import android.content.pm.IPackageInstallObserver2;
@@ -92,7 +91,6 @@
 import android.content.pm.InstantAppInfo;
 import android.content.pm.InstantAppRequest;
 import android.content.pm.ModuleInfo;
-import android.content.pm.PackageChangeEvent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
@@ -376,6 +374,7 @@
     static final int SCAN_AS_SYSTEM_EXT = 1 << 21;
     static final int SCAN_AS_ODM = 1 << 22;
     static final int SCAN_AS_APK_IN_APEX = 1 << 23;
+    static final int SCAN_AS_FACTORY = 1 << 24;
 
     @IntDef(flag = true, prefix = { "SCAN_" }, value = {
             SCAN_NO_DEX,
@@ -690,10 +689,6 @@
 
     private @NonNull final OverlayConfig mOverlayConfig;
 
-    @GuardedBy("itself")
-    final ArrayList<IPackageChangeObserver> mPackageChangeObservers =
-        new ArrayList<>();
-
     // Cached parsed flag value. Invalidated on each flag change.
     PerUidReadTimeouts[] mPerUidReadTimeoutsCache;
 
@@ -1842,10 +1837,10 @@
         mDexOptHelper = new DexOptHelper(this);
         mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
                 mProtectedPackages);
-        mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
-                mRemovePackageHelper);
         mDistractingPackageHelper = new DistractingPackageHelper(this, mInjector, mBroadcastHelper,
                 mSuspendPackageHelper);
+        mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
+                mRemovePackageHelper);
 
         synchronized (mLock) {
             // Create the computer as soon as the state objects have been installed.  The
@@ -3141,23 +3136,6 @@
         });
     }
 
-    void notifyPackageChangeObservers(PackageChangeEvent event) {
-        try {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
-            synchronized (mPackageChangeObservers) {
-                for (IPackageChangeObserver observer : mPackageChangeObservers) {
-                    try {
-                        observer.onPackageChanged(event);
-                    } catch (RemoteException e) {
-                        Log.wtf(TAG, e);
-                    }
-                }
-            }
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-    }
-
     VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) {
         if (pkg.isExternalStorage()) {
             if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
@@ -6270,11 +6248,12 @@
         }
 
         @Override
-        public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
-                @Nullable OverlayPaths overlayPaths,
-                @NonNull Set<String> outUpdatedPackageNames) {
-            return PackageManagerService.this.setEnabledOverlayPackages(userId, targetPackageName,
-                    overlayPaths, outUpdatedPackageNames);
+        public void setEnabledOverlayPackages(int userId,
+                @NonNull ArrayMap<String, OverlayPaths> pendingChanges,
+                @NonNull Set<String> outUpdatedPackageNames,
+                @NonNull Set<String> outInvalidPackageNames) {
+            PackageManagerService.this.setEnabledOverlayPackages(userId,
+                    pendingChanges, outUpdatedPackageNames, outInvalidPackageNames);
         }
 
         @Override
@@ -6482,85 +6461,119 @@
         }
     }
 
-    private boolean setEnabledOverlayPackages(@UserIdInt int userId,
-            @NonNull String targetPackageName, @Nullable OverlayPaths newOverlayPaths,
-            @NonNull Set<String> outUpdatedPackageNames) {
+    private void setEnabledOverlayPackages(@UserIdInt int userId,
+            @NonNull ArrayMap<String, OverlayPaths> pendingChanges,
+            @NonNull Set<String> outUpdatedPackageNames,
+            @NonNull Set<String> outInvalidPackageNames) {
+        final ArrayMap<String, ArrayMap<String, ArraySet<String>>>
+                targetPkgToLibNameToModifiedDependents = new ArrayMap<>();
+        final int numberOfPendingChanges = pendingChanges.size();
+
         synchronized (mOverlayPathsLock) {
-            final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents = new ArrayMap<>();
             Computer computer = snapshotComputer();
-            final PackageStateInternal packageState = computer.getPackageStateInternal(
-                    targetPackageName);
-            final AndroidPackage targetPkg = packageState == null ? null : packageState.getPkg();
-            if (targetPackageName == null || targetPkg == null) {
-                Slog.e(TAG, "failed to find package " + targetPackageName);
-                return false;
-            }
+            for (int i = 0; i < numberOfPendingChanges; i++) {
+                final String targetPackageName = pendingChanges.keyAt(i);
+                final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i);
+                final PackageStateInternal packageState = computer.getPackageStateInternal(
+                        targetPackageName);
+                final AndroidPackage targetPkg =
+                        packageState == null ? null : packageState.getPkg();
+                if (targetPackageName == null || targetPkg == null) {
+                    Slog.e(TAG, "failed to find package " + targetPackageName);
+                    outInvalidPackageNames.add(targetPackageName);
+                    continue;
+                }
 
-            if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
-                    newOverlayPaths)) {
-                return true;
-            }
+                if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
+                        newOverlayPaths)) {
+                    continue;
+                }
 
-            if (targetPkg.getLibraryNames() != null) {
-                // Set the overlay paths for dependencies of the shared library.
-                for (final String libName : targetPkg.getLibraryNames()) {
-                    ArraySet<String> modifiedDependents = null;
+                if (targetPkg.getLibraryNames() != null) {
+                    // Set the overlay paths for dependencies of the shared library.
+                    for (final String libName : targetPkg.getLibraryNames()) {
+                        ArraySet<String> modifiedDependents = null;
 
-                    final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
-                            SharedLibraryInfo.VERSION_UNDEFINED);
-                    if (info == null) {
-                        continue;
-                    }
-                    final List<VersionedPackage> dependents = computer
-                            .getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID, userId);
-                    if (dependents == null) {
-                        continue;
-                    }
-                    for (final VersionedPackage dependent : dependents) {
-                        final PackageStateInternal dependentState =
-                                computer.getPackageStateInternal(dependent.getPackageName());
-                        if (dependentState == null) {
+                        final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
+                                SharedLibraryInfo.VERSION_UNDEFINED);
+                        if (info == null) {
                             continue;
                         }
-                        if (!Objects.equals(dependentState.getUserStateOrDefault(userId)
-                                .getSharedLibraryOverlayPaths()
-                                .get(libName), newOverlayPaths)) {
-                            String dependentPackageName = dependent.getPackageName();
-                            modifiedDependents = ArrayUtils.add(modifiedDependents,
-                                    dependentPackageName);
-                            outUpdatedPackageNames.add(dependentPackageName);
+                        final List<VersionedPackage> dependents =
+                                computer.getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID,
+                                        userId);
+                        if (dependents == null) {
+                            continue;
+                        }
+                        for (final VersionedPackage dependent : dependents) {
+                            final PackageStateInternal dependentState =
+                                    computer.getPackageStateInternal(dependent.getPackageName());
+                            if (dependentState == null) {
+                                continue;
+                            }
+                            if (!Objects.equals(dependentState.getUserStateOrDefault(userId)
+                                    .getSharedLibraryOverlayPaths()
+                                    .get(libName), newOverlayPaths)) {
+                                String dependentPackageName = dependent.getPackageName();
+                                modifiedDependents = ArrayUtils.add(modifiedDependents,
+                                        dependentPackageName);
+                                outUpdatedPackageNames.add(dependentPackageName);
+                            }
+                        }
+
+                        if (modifiedDependents != null) {
+                            ArrayMap<String, ArraySet<String>> libNameToModifiedDependents =
+                                    targetPkgToLibNameToModifiedDependents.get(
+                                            targetPackageName);
+                            if (libNameToModifiedDependents == null) {
+                                libNameToModifiedDependents = new ArrayMap<>();
+                                targetPkgToLibNameToModifiedDependents.put(targetPackageName,
+                                        libNameToModifiedDependents);
+                            }
+                            libNameToModifiedDependents.put(libName, modifiedDependents);
                         }
                     }
-
-                    if (modifiedDependents != null) {
-                        libNameToModifiedDependents.put(libName, modifiedDependents);
-                    }
                 }
+
+                outUpdatedPackageNames.add(targetPackageName);
             }
 
-            outUpdatedPackageNames.add(targetPackageName);
-
             commitPackageStateMutation(null, mutator -> {
-                mutator.forPackage(targetPackageName)
-                        .userState(userId)
-                        .setOverlayPaths(newOverlayPaths);
+                for (int i = 0; i < numberOfPendingChanges; i++) {
+                    final String targetPackageName = pendingChanges.keyAt(i);
+                    final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i);
 
-                for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size(); mapIndex++) {
-                    String libName = libNameToModifiedDependents.keyAt(mapIndex);
-                    ArraySet<String> modifiedDependents =
-                            libNameToModifiedDependents.valueAt(mapIndex);
-                    for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) {
-                        mutator.forPackage(modifiedDependents.valueAt(setIndex))
-                                .userState(userId)
-                                .setOverlayPathsForLibrary(libName, newOverlayPaths);
+                    if (!outUpdatedPackageNames.contains(targetPackageName)) {
+                        continue;
+                    }
+
+                    mutator.forPackage(targetPackageName)
+                            .userState(userId)
+                            .setOverlayPaths(newOverlayPaths);
+
+                    final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents =
+                            targetPkgToLibNameToModifiedDependents.get(
+                                    targetPackageName);
+                    if (libNameToModifiedDependents == null) {
+                        continue;
+                    }
+
+                    for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size();
+                            mapIndex++) {
+                        String libName = libNameToModifiedDependents.keyAt(mapIndex);
+                        ArraySet<String> modifiedDependents =
+                                libNameToModifiedDependents.valueAt(mapIndex);
+                        for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) {
+                            mutator.forPackage(modifiedDependents.valueAt(setIndex))
+                                    .userState(userId)
+                                    .setOverlayPathsForLibrary(libName, newOverlayPaths);
+                        }
                     }
                 }
             });
         }
 
         invalidatePackageInfoCache();
-
-        return true;
     }
 
     private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 16829e0e8..f44d922 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -115,6 +115,6 @@
     public ResolveIntentHelper resolveIntentHelper;
     public DexOptHelper dexOptHelper;
     public SuspendPackageHelper suspendPackageHelper;
-    public StorageEventHelper storageEventHelper;
     public DistractingPackageHelper distractingPackageHelper;
+    public StorageEventHelper storageEventHelper;
 }
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 2016fc3..8302c2b 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -167,7 +167,7 @@
         }
         return new VerificationParams(user, session.stageDir, observer, session.params,
                 session.getInstallSource(), session.getInstallerUid(), session.getSigningDetails(),
-                session.sessionId, session.getPackageLite(), mPm);
+                session.sessionId, session.getPackageLite(), session.getUserActionRequired(), mPm);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2bae00f..f84db1f 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1227,6 +1227,11 @@
         return pkgState.isUpdatedSystemApp();
     }
 
+    @Override
+    public boolean isApkInUpdatedApex() {
+        return pkgState.isApkInUpdatedApex();
+    }
+
     public PackageSetting setDomainSetId(@NonNull UUID domainSetId) {
         mDomainSetId = domainSetId;
         onChanged();
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b53cfc5..383e4a5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -799,7 +799,6 @@
         // always make sure the system package code and resource paths dont change
         if (dp == null && p.getPkg() != null && p.getPkg().isSystem()
                 && !p.getPkgState().isUpdatedSystemApp()) {
-            p.getPkgState().setUpdatedSystemApp(true);
             final PackageSetting disabled;
             if (replaced) {
                 // a little trick...  when we install the new package, we don't
@@ -810,6 +809,7 @@
             } else {
                 disabled = p;
             }
+            p.getPkgState().setUpdatedSystemApp(true);
             mDisabledSysPackages.put(name, disabled);
             SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(disabled);
             if (sharedUserSetting != null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index b3723fb..f57eaae 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -894,8 +894,12 @@
 
         // Get the list of all dynamic shortcuts in this package.
         final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+        // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are
+        // included in the result
         findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
-                ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
+                ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
+                mShortcutUser.mService.mContext.getPackageName(),
+                0, /*getPinnedByAnyLauncher=*/ false);
 
         final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
         for (int i = 0; i < shortcuts.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index ee0fdc0..b6e633c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1873,6 +1873,44 @@
     }
 
     @Override
+    public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) {
+        checkCreateUsersPermission("update ephemeral user flag");
+        UserData userToUpdate = null;
+        synchronized (mPackagesLock) {
+            synchronized (mUsersLock) {
+                final UserData userData = mUsers.get(userId);
+                if (userData == null) {
+                    Slog.e(LOG_TAG, "User not found for setting ephemeral mode: u" + userId);
+                    return false;
+                }
+                boolean isEphemeralUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL) != 0;
+                boolean isEphemeralOnCreateUser =
+                        (userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0;
+                // when user is created in ephemeral mode via FLAG_EPHEMERAL
+                // its state cannot be changed to non ephemeral.
+                // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state
+                if (isEphemeralOnCreateUser && !enableEphemeral) {
+                    Slog.e(LOG_TAG, "Failed to change user state to non-ephemeral for user "
+                            + userId);
+                    return false;
+                }
+                if (isEphemeralUser != enableEphemeral) {
+                    if (enableEphemeral) {
+                        userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
+                    } else {
+                        userData.info.flags &= ~UserInfo.FLAG_EPHEMERAL;
+                    }
+                    userToUpdate = userData;
+                }
+            }
+            if (userToUpdate != null) {
+                writeUserLP(userToUpdate);
+            }
+        }
+        return true;
+    }
+
+    @Override
     public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) {
         try {
             checkManageUsersPermission("update users");
@@ -3979,6 +4017,10 @@
                         flags &= ~UserInfo.FLAG_EPHEMERAL;
                     }
 
+                    if ((flags & UserInfo.FLAG_EPHEMERAL) != 0) {
+                        flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE;
+                    }
+
                     userInfo = new UserInfo(userId, name, null, flags, userType);
                     userInfo.serialNumber = mNextSerialNumber++;
                     userInfo.creationTime = getCreationTime();
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index 7423bf6..a333560 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -123,6 +123,7 @@
     final long mRequiredInstalledVersionCode;
     final int mDataLoaderType;
     final int mSessionId;
+    final boolean mUserActionRequired;
 
     private boolean mWaitForVerificationToComplete;
     private boolean mWaitForIntegrityVerificationToComplete;
@@ -135,7 +136,7 @@
     VerificationParams(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
             PackageInstaller.SessionParams sessionParams, InstallSource installSource,
             int installerUid, SigningDetails signingDetails, int sessionId, PackageLite lite,
-            PackageManagerService pm) {
+            boolean userActionRequired, PackageManagerService pm) {
         super(user, pm);
         mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
         mObserver = observer;
@@ -154,6 +155,7 @@
                 ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
         mSessionId = sessionId;
         mPackageLite = lite;
+        mUserActionRequired = userActionRequired;
     }
 
     @Override
@@ -430,6 +432,8 @@
 
         verification.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
 
+        verification.putExtra(PackageManager.EXTRA_USER_ACTION_REQUIRED, mUserActionRequired);
+
         populateInstallerExtras(verification);
 
         // Streaming installation timeout schema is enabled only for:
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 849f530..16cf721 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1022,7 +1022,8 @@
         }
         for (String packageName : packageNames) {
             grantPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId,
-                    PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS,
+                    PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS);
+            grantPermissionsToPackage(NO_PM_CACHE, packageName, userId, false, false,
                     NOTIFICATION_PERMISSIONS);
         }
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 5a05134b..5d6ebec 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -277,10 +277,6 @@
     }
 
     private boolean checkAutoRevokeAccess(AndroidPackage pkg, int callingUid) {
-        if (pkg == null) {
-            return false;
-        }
-
         final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission(
                 Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS)
                 == PackageManager.PERMISSION_GRANTED;
@@ -292,6 +288,12 @@
                     + Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS
                     + " or be the installer on record");
         }
+
+        if (pkg == null || mPackageManagerInt.filterAppAccess(pkg, callingUid,
+                UserHandle.getUserId(callingUid))) {
+            return false;
+        }
+
         return true;
     }
 
@@ -301,9 +303,6 @@
 
         final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
         final int callingUid = Binder.getCallingUid();
-        if (mPackageManagerInt.filterAppAccess(packageName, callingUid, userId)) {
-            return false;
-        }
 
         if (!checkAutoRevokeAccess(pkg, callingUid)) {
             return false;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index c524fb7..a963280 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -47,7 +47,6 @@
 import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
 import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED;
 
-import static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
 import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
@@ -3240,9 +3239,7 @@
         }
         // Only enforce the allowlist on boot
         if (!mSystemReady) {
-            final boolean isInUpdatedApex = containingApexPackageName != null
-                    && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName,
-                    MATCH_ACTIVE_PACKAGE));
+            final boolean isInUpdatedApex = packageSetting.isApkInUpdatedApex();
             // Apps that are in updated apexs' do not need to be allowlisted
             if (!isInUpdatedApex) {
                 Slog.w(TAG, "Privileged permission " + permissionName + " for package "
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 7726d7f..b5e0e44 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -303,6 +303,11 @@
     boolean isUpdatedSystemApp();
 
     /**
+     * Whether this app is packaged in an updated apex.
+     */
+    boolean isApkInUpdatedApex();
+
+    /**
      * @see AndroidPackageApi#isVendor()
      */
     boolean isVendor();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 3170304..878a837 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -71,6 +71,7 @@
                 INSTALL_PERMISSIONS_FIXED,
                 UPDATE_AVAILABLE,
                 UPDATED_SYSTEM_APP,
+                APK_IN_UPDATED_APEX,
         })
         public @interface Flags {
         }
@@ -89,6 +90,7 @@
         private static final int INSTALL_PERMISSIONS_FIXED = 1 << 11;
         private static final int UPDATE_AVAILABLE = 1 << 12;
         private static final int UPDATED_SYSTEM_APP = 1 << 13;
+        private static final int APK_IN_UPDATED_APEX = 1 << 14;
     }
 
     private int mBooleans;
@@ -187,6 +189,7 @@
         setBoolean(Booleans.UPDATE_AVAILABLE, pkgState.isUpdateAvailable());
         mLastPackageUsageTime = pkgState.getLastPackageUsageTime();
         setBoolean(Booleans.UPDATED_SYSTEM_APP, pkgState.isUpdatedSystemApp());
+        setBoolean(Booleans.APK_IN_UPDATED_APEX, pkgState.isApkInUpdatedApex());
         mSigningInfo = pkgState.getSigningInfo();
 
         SparseArray<? extends PackageUserState> userStates = pkgState.getUserStates();
@@ -264,6 +267,11 @@
     }
 
     @Override
+    public boolean isApkInUpdatedApex() {
+        return getBoolean(Booleans.APK_IN_UPDATED_APEX);
+    }
+
+    @Override
     public boolean isVendor() {
         return getBoolean(Booleans.VENDOR);
     }
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index 7bd720a..fad2f85 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -52,6 +52,8 @@
     private List<String> usesLibraryFiles = emptyList();
 
     private boolean updatedSystemApp;
+    private boolean apkInApex;
+    private boolean apkInUpdatedApex;
 
     @NonNull
     private volatile long[] lastPackageUsageTimeInMills;
@@ -116,6 +118,8 @@
         }
 
         this.updatedSystemApp = other.updatedSystemApp;
+        this.apkInApex = other.apkInApex;
+        this.apkInUpdatedApex = other.apkInUpdatedApex;
         this.lastPackageUsageTimeInMills = other.lastPackageUsageTimeInMills;
         this.overrideSeInfo = other.overrideSeInfo;
         mPackageSetting.onChanged();
@@ -150,6 +154,18 @@
         return this;
     }
 
+    public PackageStateUnserialized setApkInApex(boolean value) {
+        apkInApex = value;
+        mPackageSetting.onChanged();
+        return this;
+    }
+
+    public PackageStateUnserialized setApkInUpdatedApex(boolean value) {
+        apkInUpdatedApex = value;
+        mPackageSetting.onChanged();
+        return this;
+    }
+
     public PackageStateUnserialized setLastPackageUsageTimeInMills(@NonNull long... value) {
         lastPackageUsageTimeInMills = value;
         mPackageSetting.onChanged();
@@ -198,6 +214,16 @@
     }
 
     @DataClass.Generated.Member
+    public boolean isApkInApex() {
+        return apkInApex;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isApkInUpdatedApex() {
+        return apkInUpdatedApex;
+    }
+
+    @DataClass.Generated.Member
     public @NonNull long[] getLastPackageUsageTimeInMills() {
         long[] _lastPackageUsageTimeInMills = lastPackageUsageTimeInMills;
         if (_lastPackageUsageTimeInMills == null) {
@@ -222,10 +248,10 @@
     }
 
     @DataClass.Generated(
-            time = 1642554781099L,
+            time = 1646203523807L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java",
-            inputSignatures = "private  boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate  boolean updatedSystemApp\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\nprivate  long[] lazyInitLastPackageUsageTimeInMills()\npublic  com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic  long getLatestPackageUseTimeInMills()\npublic  long getLatestForegroundPackageUseTimeInMills()\npublic  void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic  com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
+            inputSignatures = "private  boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate  boolean updatedSystemApp\nprivate  boolean apkInApex\nprivate  boolean apkInUpdatedApex\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate final @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\nprivate  long[] lazyInitLastPackageUsageTimeInMills()\npublic  com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic  long getLatestPackageUseTimeInMills()\npublic  long getLatestForegroundPackageUseTimeInMills()\npublic  void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic  com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setApkInApex(boolean)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setApkInUpdatedApex(boolean)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
index 91d2010..8a64884 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
@@ -321,19 +321,15 @@
 
             pi.applicationInfo.sourceDir = apexFile.getPath();
             pi.applicationInfo.publicSourceDir = apexFile.getPath();
+            pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+            pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
             if (apexInfo.isFactory) {
-                pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
                 pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             } else {
-                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
                 pi.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
-            if (apexInfo.isActive) {
-                pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
-            } else {
-                pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
-            }
             pi.isApex = true;
+            pi.isActiveApex = apexInfo.isActive;
         }
 
         final SigningDetails signingDetails = pkg.getSigningDetails();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ad56f19..6e78ecb 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -524,9 +524,6 @@
     // The screen off timeout setting value in milliseconds.
     private long mScreenOffTimeoutSetting;
 
-    // The screen off timeout setting value in milliseconds to apply while device is docked.
-    private long mScreenOffTimeoutDockedSetting;
-
     // Default for attentive warning duration.
     private long mAttentiveWarningDurationConfig;
 
@@ -1289,9 +1286,6 @@
         resolver.registerContentObserver(Settings.System.getUriFor(
                 Settings.System.SCREEN_OFF_TIMEOUT),
                 false, mSettingsObserver, UserHandle.USER_ALL);
-        resolver.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.SCREEN_OFF_TIMEOUT_DOCKED),
-                false, mSettingsObserver, UserHandle.USER_ALL);
         resolver.registerContentObserver(Settings.Secure.getUriFor(
                 Settings.Secure.SLEEP_TIMEOUT),
                 false, mSettingsObserver, UserHandle.USER_ALL);
@@ -1414,9 +1408,6 @@
         mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver,
                 Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT,
                 UserHandle.USER_CURRENT);
-        mScreenOffTimeoutDockedSetting = Settings.System.getLongForUser(resolver,
-                Settings.System.SCREEN_OFF_TIMEOUT_DOCKED, mScreenOffTimeoutSetting,
-                UserHandle.USER_CURRENT);
         mSleepTimeoutSetting = Settings.Secure.getIntForUser(resolver,
                 Settings.Secure.SLEEP_TIMEOUT, DEFAULT_SLEEP_TIMEOUT,
                 UserHandle.USER_CURRENT);
@@ -2999,9 +2990,7 @@
 
     @GuardedBy("mLock")
     private long getScreenOffTimeoutLocked(long sleepTimeout, long attentiveTimeout) {
-        long timeout = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED
-                ? mScreenOffTimeoutSetting
-                : mScreenOffTimeoutDockedSetting;
+        long timeout = mScreenOffTimeoutSetting;
         if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
             timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin);
         }
@@ -4946,8 +4935,7 @@
         }
     }
 
-    @VisibleForTesting
-    final class DockReceiver extends BroadcastReceiver {
+    private final class DockReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (mLock) {
diff --git a/services/core/java/com/android/server/power/ShutdownCheckPoints.java b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
index 05ee7df..1a9eae6 100644
--- a/services/core/java/com/android/server/power/ShutdownCheckPoints.java
+++ b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
@@ -36,7 +36,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
-import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -56,7 +55,7 @@
     private static final SimpleDateFormat DATE_FORMAT =
             new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
 
-    private final LinkedList<CheckPoint> mCheckPoints;
+    private final ArrayList<CheckPoint> mCheckPoints;
     private final Injector mInjector;
 
     private ShutdownCheckPoints() {
@@ -85,7 +84,7 @@
 
     @VisibleForTesting
     ShutdownCheckPoints(Injector injector) {
-        mCheckPoints = new LinkedList<>();
+        mCheckPoints = new ArrayList<>();
         mInjector = injector;
     }
 
@@ -144,8 +143,8 @@
 
     private void recordCheckPointInternal(CheckPoint checkPoint) {
         synchronized (mCheckPoints) {
-            mCheckPoints.addLast(checkPoint);
-            if (mCheckPoints.size() > mInjector.maxCheckPoints()) mCheckPoints.removeFirst();
+            mCheckPoints.add(checkPoint);
+            if (mCheckPoints.size() > mInjector.maxCheckPoints()) mCheckPoints.remove(0);
         }
     }
 
diff --git a/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java b/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java
index 3543e93..928d128 100644
--- a/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java
+++ b/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java
@@ -32,6 +32,7 @@
 import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
 import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
 
+import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
@@ -147,7 +148,7 @@
     @NonNull
     static AndroidKeystoreAttestationVerificationAttributes fromCertificate(
             @NonNull X509Certificate x509Certificate)
-            throws Exception {
+            throws CertificateEncodingException, IOException {
         return new AndroidKeystoreAttestationVerificationAttributes(x509Certificate);
     }
 
@@ -281,7 +282,7 @@
     }
 
     private AndroidKeystoreAttestationVerificationAttributes(X509Certificate x509Certificate)
-            throws Exception {
+            throws CertificateEncodingException, IOException {
         Certificate certificate = Certificate.getInstance(
                 new ASN1InputStream(x509Certificate.getEncoded()).readObject());
         ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions()
@@ -380,7 +381,7 @@
     }
 
     private void parseAttestationApplicationId(byte [] attestationApplicationId)
-            throws Exception {
+            throws IOException {
         ASN1Sequence outerSequence = ASN1Sequence.getInstance(
                 new ASN1InputStream(attestationApplicationId).readObject());
         Map<String, Long> packageNameVersion = new HashMap<>();
diff --git a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
index 0f8be5a..bcc39ba 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
@@ -22,8 +22,10 @@
 import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
 import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE;
 import static android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY;
+import static android.security.attestationverification.AttestationVerificationManager.localBindingTypeToString;
 
 import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED;
+import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.fromCertificate;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -31,6 +33,7 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
+import android.security.attestationverification.AttestationVerificationManager.LocalBindingType;
 import android.util.Log;
 import android.util.Slog;
 
@@ -40,8 +43,10 @@
 import org.json.JSONObject;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.cert.CertPath;
 import java.security.cert.CertPathValidator;
 import java.security.cert.CertPathValidatorException;
@@ -65,17 +70,26 @@
 import java.util.Set;
 
 /**
- * Verifies Android key attestation according to the {@code PROFILE_PEER_DEVICE} profile.
+ * Verifies Android key attestation according to the
+ * {@link android.security.attestationverification.AttestationVerificationManager#PROFILE_PEER_DEVICE PROFILE_PEER_DEVICE}
+ * profile.
  *
- * Trust anchors are vendor-defined via the vendor_required_attestation_certificates.xml resource.
+ * <p>
  * The profile is satisfied by checking all the following:
- * * TrustAnchor match
- * * Certificate validity
- * * Android OS 10 or higher
- * * Hardware backed key store
- * * Verified boot locked
- * * Remote Patch level must be within 1 year of local patch if local patch is less than 1 year old.
+ * <ul>
+ * <li> TrustAnchor match
+ * <li> Certificate validity
+ * <li> Android OS 10 or higher
+ * <li> Hardware backed key store
+ * <li> Verified boot locked
+ * <li> Remote Patch level must be within 1 year of local patch if local patch is less than 1 year
+ * old.
+ * </ul>
  *
+ * <p>
+ * Trust anchors are vendor-defined by populating
+ * {@link R.array#vendor_required_attestation_certificates} string array (defenined in
+ * {@code frameworks/base/core/res/res/values/vendor_required_attestation_certificates.xml}).
  */
 class AttestationVerificationPeerDeviceVerifier {
     private static final String TAG = "AVF";
@@ -87,20 +101,8 @@
     private final boolean mRevocationEnabled;
     private final LocalDate mTestSystemDate;
     private final LocalDate mTestLocalPatchDate;
-    private CertificateFactory mCertificateFactory;
-    private CertPathValidator mCertPathValidator;
-
-    private static void debugVerboseLog(String str, Throwable t) {
-        if (DEBUG) {
-            Slog.v(TAG, str, t);
-        }
-    }
-
-    private static void debugVerboseLog(String str) {
-        if (DEBUG) {
-            Slog.v(TAG, str);
-        }
-    }
+    private final CertificateFactory mCertificateFactory;
+    private final CertPathValidator mCertPathValidator;
 
     AttestationVerificationPeerDeviceVerifier(@NonNull Context context) throws Exception {
         mContext = Objects.requireNonNull(context);
@@ -128,53 +130,71 @@
 
     /**
      * Verifies attestation for public key or challenge local binding.
-     *
+     * <p>
      * The attestations must be suitable for {@link java.security.cert.CertificateFactory}
      * The certificates in the attestation provided must be DER-encoded and may be supplied in
      * binary or printable (Base64) encoding. If the certificate is provided in Base64 encoding,
-     * it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and must be bounded at
-     * the end by -----END CERTIFICATE-----.
+     * it must be bounded at the beginning by {@code -----BEGIN CERTIFICATE-----}, and must be
+     * bounded at the end by {@code -----END CERTIFICATE-----}.
      *
      * @param localBindingType Only {@code TYPE_PUBLIC_KEY} and {@code TYPE_CHALLENGE} supported.
      * @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported.
      * @param attestation Certificates should be DER encoded with leaf certificate appended first.
      */
     int verifyAttestation(
-            int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation) {
-        int status = RESULT_FAILURE;
-
+            @LocalBindingType int localBindingType,
+            @NonNull Bundle requirements,
+            @NonNull byte[] attestation) {
         if (mCertificateFactory == null) {
-            debugVerboseLog("Was unable to initialize CertificateFactory onCreate.");
-            return status;
+            debugVerboseLog("Unable to access CertificateFactory");
+            return RESULT_FAILURE;
         }
 
         if (mCertPathValidator == null) {
-            debugVerboseLog("Was unable to initialize CertPathValidator onCreate.");
-            return status;
+            debugVerboseLog("Unable to access CertPathValidator");
+            return RESULT_FAILURE;
         }
 
-        List<X509Certificate> certificates;
+        // Check if the provided local binding type is supported and if the provided requirements
+        // "match" the binding type.
+        if (!validateAttestationParameters(localBindingType, requirements)) {
+            return RESULT_FAILURE;
+        }
+
         try {
-            certificates = getCertificates(attestation);
-        } catch (CertificateException e) {
-            debugVerboseLog("Unable to parse attestation certificates.", e);
-            return status;
-        }
+            // First: parse and validate the certificate chain.
+            final List<X509Certificate> certificateChain = getCertificates(attestation);
+            // (returns void, but throws CertificateException and other similar Exceptions)
+            validateCertificateChain(certificateChain);
 
-        if (certificates.isEmpty()) {
-            debugVerboseLog("Attestation contains no certificates.");
-            return status;
-        }
+            final var leafCertificate = certificateChain.get(0);
+            final var attestationExtension = fromCertificate(leafCertificate);
 
-        X509Certificate leafNode = certificates.get(0);
-        if (validateRequirements(localBindingType, requirements)
-                && validateCertificateChain(certificates)
-                && checkCertificateAttributes(leafNode, localBindingType, requirements)) {
-            status = RESULT_SUCCESS;
-        } else {
-            status = RESULT_FAILURE;
+            // Second: verify if the attestation satisfies the "peer device" porfile.
+            if (!checkAttestationForPeerDeviceProfile(attestationExtension)) {
+                return RESULT_FAILURE;
+            }
+
+            // Third: check if the attestation satisfies local binding requirements.
+            if (!checkLocalBindingRequirements(
+                    leafCertificate, attestationExtension, localBindingType, requirements)) {
+                return RESULT_FAILURE;
+            }
+
+            return RESULT_SUCCESS;
+        } catch (CertificateException | CertPathValidatorException
+                | InvalidAlgorithmParameterException | IOException e) {
+            // Catch all non-RuntimeExpceptions (all of these are thrown by either getCertificates()
+            // or validateCertificateChain() or
+            // AndroidKeystoreAttestationVerificationAttributes.fromCertificate())
+            debugVerboseLog("Unable to parse/validate Android Attestation certificate(s)", e);
+            return RESULT_FAILURE;
+        } catch (RuntimeException e) {
+            // Catch everyting else (RuntimeExpcetions), since we don't want to throw any exceptions
+            // out of this class/method.
+            debugVerboseLog("Unexpected error", e);
+            return RESULT_FAILURE;
         }
-        return status;
     }
 
     @NonNull
@@ -189,14 +209,19 @@
         return certificates;
     }
 
-    private boolean validateRequirements(int localBindingType, Bundle requirements) {
-        if (requirements.size() != 1) {
-            debugVerboseLog("Requirements does not contain exactly 1 key.");
+    /**
+     * Check if the {@code localBindingType} is supported and if the {@code requirements} contains
+     * the required parameter for the given {@code @LocalBindingType}.
+     */
+    private boolean validateAttestationParameters(
+            @LocalBindingType int localBindingType, @NonNull Bundle requirements) {
+        if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) {
+            debugVerboseLog("Binding type is not supported: " + localBindingType);
             return false;
         }
 
-        if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) {
-            debugVerboseLog("Binding type is not supported: " + localBindingType);
+        if (requirements.size() != 1) {
+            debugVerboseLog("Requirements does not contain exactly 1 key.");
             return false;
         }
 
@@ -213,29 +238,25 @@
         return true;
     }
 
-    private boolean validateCertificateChain(List<X509Certificate> certificates) {
+    private void validateCertificateChain(List<X509Certificate> certificates)
+            throws CertificateException, CertPathValidatorException,
+            InvalidAlgorithmParameterException  {
         if (certificates.size() < 2) {
             debugVerboseLog("Certificate chain less than 2 in size.");
-            return false;
+            throw new CertificateException("Certificate chain less than 2 in size.");
         }
 
-        try {
-            CertPath certificatePath = mCertificateFactory.generateCertPath(certificates);
-            PKIXParameters validationParams = new PKIXParameters(mTrustAnchors);
-            if (mRevocationEnabled) {
-                // Checks Revocation Status List based on
-                // https://developer.android.com/training/articles/security-key-attestation#certificate_status
-                PKIXCertPathChecker checker = new AndroidRevocationStatusListChecker();
-                validationParams.addCertPathChecker(checker);
-            }
-            // Do not use built-in revocation status checker.
-            validationParams.setRevocationEnabled(false);
-            mCertPathValidator.validate(certificatePath, validationParams);
-        } catch (Throwable t) {
-            debugVerboseLog("Invalid certificate chain.", t);
-            return false;
+        CertPath certificatePath = mCertificateFactory.generateCertPath(certificates);
+        PKIXParameters validationParams = new PKIXParameters(mTrustAnchors);
+        if (mRevocationEnabled) {
+            // Checks Revocation Status List based on
+            // https://developer.android.com/training/articles/security-key-attestation#certificate_status
+            PKIXCertPathChecker checker = new AndroidRevocationStatusListChecker();
+            validationParams.addCertPathChecker(checker);
         }
-        return true;
+        // Do not use built-in revocation status checker.
+        validationParams.setRevocationEnabled(false);
+        mCertPathValidator.validate(certificatePath, validationParams);
     }
 
     private Set<TrustAnchor> getTrustAnchors() throws CertPathValidatorException {
@@ -267,18 +288,44 @@
                 R.array.vendor_required_attestation_certificates);
     }
 
-    private boolean checkCertificateAttributes(
-            X509Certificate leafCertificate, int localBindingType, Bundle requirements) {
-        AndroidKeystoreAttestationVerificationAttributes attestationAttributes;
-        try {
-            attestationAttributes =
-                    AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
-                            leafCertificate);
-        } catch (Throwable t) {
-            debugVerboseLog("Could not get ParsedAttestationAttributes from Certificate.", t);
-            return false;
+    private boolean checkLocalBindingRequirements(
+            @NonNull X509Certificate leafCertificate,
+            @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes,
+            @LocalBindingType int localBindingType,
+            @NonNull Bundle requirements) {
+        switch (localBindingType) {
+            case TYPE_PUBLIC_KEY:
+                // Verify leaf public key matches provided public key.
+                final boolean publicKeyMatches = checkPublicKey(
+                        leafCertificate, requirements.getByteArray(PARAM_PUBLIC_KEY));
+                if (!publicKeyMatches) {
+                    debugVerboseLog(
+                            "Provided public key does not match leaf certificate public key.");
+                    return false;
+                }
+                break;
+
+            case TYPE_CHALLENGE:
+                // Verify challenge matches provided challenge.
+                final boolean attestationChallengeMatches = checkAttestationChallenge(
+                        attestationAttributes, requirements.getByteArray(PARAM_CHALLENGE));
+                if (!attestationChallengeMatches) {
+                    debugVerboseLog(
+                            "Provided challenge does not match leaf certificate challenge.");
+                    return false;
+                }
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unsupported local binding type "
+                        + localBindingTypeToString(localBindingType));
         }
 
+        return true;
+    }
+
+    private boolean checkAttestationForPeerDeviceProfile(
+            @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes) {
         // Checks for support of Keymaster 4.
         if (attestationAttributes.getAttestationVersion() < 3) {
             debugVerboseLog("Attestation version is not at least 3 (Keymaster 4).");
@@ -344,25 +391,22 @@
             return false;
         }
 
-        // Verify leaf public key matches provided public key.
-        if (localBindingType == TYPE_PUBLIC_KEY
-                && !Arrays.equals(requirements.getByteArray(PARAM_PUBLIC_KEY),
-                                  leafCertificate.getPublicKey().getEncoded())) {
-            debugVerboseLog("Provided public key does not match leaf certificate public key.");
-            return false;
-        }
-
-        // Verify challenge matches provided challenge.
-        if (localBindingType == TYPE_CHALLENGE
-                && !Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE),
-                                  attestationAttributes.getAttestationChallenge().toByteArray())) {
-            debugVerboseLog("Provided challenge does not match leaf certificate challenge.");
-            return false;
-        }
-
         return true;
     }
 
+    private boolean checkPublicKey(
+            @NonNull Certificate certificate, @NonNull byte[] expectedPublicKey) {
+        final byte[] publicKey = certificate.getPublicKey().getEncoded();
+        return Arrays.equals(publicKey, expectedPublicKey);
+    }
+
+    private boolean checkAttestationChallenge(
+            @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes,
+            @NonNull byte[] expectedChallenge) {
+        final byte[] challenge = attestationAttributes.getAttestationChallenge().toByteArray();
+        return Arrays.equals(challenge, expectedChallenge);
+    }
+
     /**
      * Validates patchLevel passed is within range of the local device patch date if local patch is
      * not over one year old. Since the time can be changed on device, just checking the patch date
@@ -507,4 +551,16 @@
                     R.string.vendor_required_attestation_revocation_list_url);
         }
     }
+
+    private static void debugVerboseLog(String str, Throwable t) {
+        if (DEBUG) {
+            Slog.v(TAG, str, t);
+        }
+    }
+
+    private static void debugVerboseLog(String str) {
+        if (DEBUG) {
+            Slog.v(TAG, str);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index e3dcfd0..881bdbd 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -65,7 +65,6 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -226,12 +225,18 @@
     }
 
     @Override
-    public int checkSlicePermission(Uri uri, String callingPkg, String pkg, int pid, int uid,
+    public int checkSlicePermission(Uri uri, String callingPkg, int pid, int uid,
             String[] autoGrantPermissions) {
+        return checkSlicePermissionInternal(uri, callingPkg, null /* pkg */, pid, uid,
+                autoGrantPermissions);
+    }
+
+    private int checkSlicePermissionInternal(Uri uri, String callingPkg, String pkg, int pid,
+            int uid, String[] autoGrantPermissions) {
         int userId = UserHandle.getUserId(uid);
         if (pkg == null) {
             for (String p : mContext.getPackageManager().getPackagesForUid(uid)) {
-                if (checkSlicePermission(uri, callingPkg, p, pid, uid, autoGrantPermissions)
+                if (checkSlicePermissionInternal(uri, callingPkg, p, pid, uid, autoGrantPermissions)
                         == PERMISSION_GRANTED) {
                     return PERMISSION_GRANTED;
                 }
@@ -395,7 +400,8 @@
     }
 
     protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
-        return checkSlicePermission(uri, null, pkg, pid, uid, null);
+        return checkSlicePermissionInternal(uri, null /* callingPkg */, pkg, pid, uid,
+                null /* autoGrantPermissions */);
     }
 
     private String getProviderPkg(Uri uri, int user) {
@@ -404,7 +410,7 @@
             String providerName = getUriWithoutUserId(uri).getAuthority();
             ProviderInfo provider = mContext.getPackageManager().resolveContentProviderAsUser(
                     providerName, 0, getUserIdFromUri(uri, user));
-            return provider.packageName;
+            return provider == null ? null : provider.packageName;
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -629,7 +635,7 @@
                 .scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(authority)
                 .build(), 0);
-        return mPermissions.getAllPackagesGranted(pkg);
+        return pkg == null ? new String[0] : mPermissions.getAllPackagesGranted(pkg);
     }
 
     /**
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index 0772503..b6ab351 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -155,8 +155,6 @@
 
     // Handler.Callback implementation
 
-    private final Queue<Message> mPendingMessageQueue = new LinkedList<>();
-
     @Override
     public boolean handleMessage(Message msg) {
         switch (msg.what) {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index f57a852..98dfb00 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -69,7 +69,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -89,7 +88,7 @@
     private final TvInputHal mHal = new TvInputHal(this);
     private final SparseArray<Connection> mConnections = new SparseArray<>();
     private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
-    private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
+    private final List<HdmiDeviceInfo> mHdmiDeviceList = new ArrayList<>();
     /* A map from a device ID to the matching TV input ID. */
     private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
     /* A map from a HDMI logical address to the matching TV input ID. */
@@ -112,9 +111,9 @@
     private int mCurrentMaxIndex = 0;
 
     private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
-    private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
+    private final List<Message> mPendingHdmiDeviceEvents = new ArrayList<>();
 
-    private final List<Message> mPendingTvinputInfoEvents = new LinkedList<>();
+    private final List<Message> mPendingTvinputInfoEvents = new ArrayList<>();
 
     // Calls to mListener should happen here.
     private final Handler mHandler = new ListenerHandler();
@@ -234,11 +233,7 @@
             } else {
                 Message msg = mHandler.obtainMessage(ListenerHandler.TVINPUT_INFO_ADDED,
                     deviceId, cableConnectionStatus, connection);
-                for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext();) {
-                    if (it.next().arg1 == deviceId) {
-                    it.remove();
-                    }
-                }
+                mPendingTvinputInfoEvents.removeIf(message -> message.arg1 == deviceId);
                 mPendingTvinputInfoEvents.add(msg);
            }
             ITvInputHardwareCallback callback = connection.getCallbackLocked();
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 7f84f61..3b8677d 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -68,6 +68,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
 import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME;
 
@@ -102,6 +103,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -771,6 +773,12 @@
             info.mReason = activityToReason.valueAt(index);
             info.mLoggedTransitionStarting = true;
             if (info.mIsDrawn) {
+                if (info.mReason == APP_TRANSITION_RECENTS_ANIM) {
+                    final LatencyTracker latencyTracker = r.mWmService.mLatencyTracker;
+                    final int duration = info.mSourceEventDelayMs + info.mCurrentTransitionDelayMs;
+                    mLoggerHandler.post(() -> latencyTracker.logAction(
+                            LatencyTracker.ACTION_START_RECENTS_ANIMATION, duration));
+                }
                 done(false /* abort */, info, "notifyTransitionStarting drawn", timestampNs);
             }
         }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index cefc871..6f13f86 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -96,8 +96,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -854,7 +854,7 @@
             boolean visible) {
 
         // The candidates of animation targets, which might be able to promote to higher level.
-        final LinkedList<WindowContainer> candidates = new LinkedList<>();
+        final ArrayDeque<WindowContainer> candidates = new ArrayDeque<>();
         final ArraySet<ActivityRecord> apps = visible ? openingApps : closingApps;
         for (int i = 0; i < apps.size(); ++i) {
             final ActivityRecord app = apps.valueAt(i);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 8a0ae65..70545c9 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -53,10 +53,10 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.WeakHashMap;
 import java.util.function.Consumer;
@@ -318,13 +318,13 @@
     private final WindowManagerGlobalLock mGlobalLock;
 
     // List of task organizers by priority
-    private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>();
+    private final ArrayDeque<ITaskOrganizer> mTaskOrganizers = new ArrayDeque<>();
     private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
     private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
     // Pending task events due to layout deferred.
     private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>();
     // Set of organized tasks (by taskId) that dispatch back pressed to their organizers
-    private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet();
+    private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet<>();
 
     private RunningTaskInfo mTmpTaskInfo;
     private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4d0dfe2..4a0b343 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -52,6 +52,7 @@
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 
@@ -670,8 +671,6 @@
 
         handleNonAppWindowsInTransition(dc, mType, mFlags);
 
-        reportStartReasonsToLogger();
-
         // The callback is only populated for custom activity-level client animations
         sendRemoteCallback(mClientAnimationStartCallback);
 
@@ -760,6 +759,8 @@
         }
         mSyncId = -1;
         mOverrideOptions = null;
+
+        reportStartReasonsToLogger();
     }
 
     /**
@@ -972,11 +973,15 @@
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
             if (r == null || !r.mVisibleRequested) continue;
+            int transitionReason = APP_TRANSITION_WINDOWS_DRAWN;
             // At this point, r is "ready", but if it's not "ALL ready" then it is probably only
             // ready due to starting-window.
-            reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
-                    && !r.mLastAllReadyAtSync)
-                    ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN);
+            if (r.mStartingData instanceof SplashScreenStartingData && !r.mLastAllReadyAtSync) {
+                transitionReason = APP_TRANSITION_SPLASH_SCREEN;
+            } else if (r.isActivityTypeHomeOrRecents() && isTransientLaunch(r)) {
+                transitionReason = APP_TRANSITION_RECENTS_ANIM;
+            }
+            reasons.put(r, transitionReason);
         }
         mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
                 reasons);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 0d49f5f..cfcbc3a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -185,6 +185,7 @@
         "android.system.suspend.control-V1-cpp",
         "android.system.suspend.control.internal-cpp",
         "android.system.suspend-V1-ndk",
+        "server_configurable_flags",
         "service.incremental",
     ],
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3c5ebe7..ffda8be 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -50,6 +50,7 @@
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <server_configurable_flags/get_flags.h>
 #include <ui/Region.h>
 #include <utils/Log.h>
 #include <utils/Looper.h>
@@ -87,6 +88,13 @@
 // where the speed ranges from -7 to + 7 and is supplied by the user.
 static const float POINTER_SPEED_EXPONENT = 1.0f / 4;
 
+// Category (=namespace) name for the input settings that are applied at boot time
+static const char* INPUT_NATIVE_BOOT = "input_native_boot";
+/**
+ * Feature flag name. This flag determines which VelocityTracker strategy is used by default.
+ */
+static const char* VELOCITYTRACKER_STRATEGY = "velocitytracker_strategy";
+
 static struct {
     jclass clazz;
     jmethodID notifyConfigurationChanged;
@@ -2120,8 +2128,10 @@
 static std::string dumpInputProperties() {
     std::string out = "Input properties:\n";
     const std::string strategy =
-            sysprop::InputProperties::velocitytracker_strategy().value_or("default");
-    out += "  persist.input.velocitytracker.strategy = " + strategy + "\n";
+            server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT,
+                                                                 VELOCITYTRACKER_STRATEGY,
+                                                                 "default");
+    out += "  velocitytracker_strategy (flag value) = " + strategy + "\n";
     out += "\n";
     return out;
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 268d588..b7005a5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11890,6 +11890,7 @@
                 ap = getParentOfAdminIfRequired(getOrganizationOwnedProfileOwnerLocked(caller),
                         parent);
             } else {
+                Preconditions.checkCallAuthorization(!isFinancedDeviceOwner(caller));
                 ap = getParentOfAdminIfRequired(getProfileOwnerOrDeviceOwnerLocked(caller), parent);
             }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index a2e813a..82334f2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -59,6 +59,7 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.server.LocalServices;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -136,14 +137,17 @@
                 .spyStatic(Settings.Global.class)
                 .startMocking();
 
-        // Mock LocalServices.getService(PackageManagerInternal.class).getPackage dependency
-        // needed by AppOpsService
+        // Mock LocalServices.getService(PackageManagerInternal.class).getPackageStateInternal
+        // and getPackage dependency needed by AppOpsService
         PackageManagerInternal mockPackageManagerInternal = mock(PackageManagerInternal.class);
+        PackageStateInternal mockMyPSInternal = mock(PackageStateInternal.class);
         AndroidPackage mockMyPkg = mock(AndroidPackage.class);
         when(mockMyPkg.isPrivileged()).thenReturn(false);
         when(mockMyPkg.getUid()).thenReturn(mMyUid);
         when(mockMyPkg.getAttributions()).thenReturn(Collections.emptyList());
 
+        when(mockPackageManagerInternal.getPackageStateInternal(sMyPackageName))
+                .thenReturn(mockMyPSInternal);
         when(mockPackageManagerInternal.getPackage(sMyPackageName)).thenReturn(mockMyPkg);
         doReturn(mockPackageManagerInternal).when(
                 () -> LocalServices.getService(PackageManagerInternal.class));
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 617321b..7755552 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -558,6 +558,40 @@
     }
 
     @Test
+    public void testAfterDisplayStateChanges_committedSetAfterState() throws Exception {
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+        DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+        // Turn off.
+        Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_OFF, 0,
+                0);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+        mListener.changedDisplays.clear();
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().state).isEqualTo(Display.STATE_OFF);
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState).isNotEqualTo(
+                Display.STATE_OFF);
+        verify(mSurfaceControlProxy, never()).setDisplayPowerMode(display.token, Display.STATE_OFF);
+
+        // Execute powerstate change.
+        changeStateRunnable.run();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+
+        // Verify that committed triggered a new change event and is set correctly.
+        verify(mSurfaceControlProxy, never()).setDisplayPowerMode(display.token, Display.STATE_OFF);
+        assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().state).isEqualTo(Display.STATE_OFF);
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState).isEqualTo(
+                Display.STATE_OFF);
+    }
+
+    @Test
     public void testAfterDisplayChange_GameContentTypeSupportIsUpdated() throws Exception {
         FakeDisplay display = new FakeDisplay(PORT_A);
         display.dynamicInfo.gameContentTypeSupported = true;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index cf6c82f..0d2e666 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -29,6 +29,7 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.never
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 
 @RunWith(JUnit4::class)
@@ -192,4 +193,70 @@
                 Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
                 nullable(), nullable(), any(), nullable(), nullable(), nullable())
     }
+
+    @Test
+    fun sendDistractingPackagesChanged_withSameVisibilityAllowList() {
+        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+        mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
+
+        distractingPackageHelper.sendDistractingPackagesChanged(pms.snapshotComputer(),
+                packagesToChange, uidsToChange, TEST_USER_ID,
+                PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
+        testHandler.flush()
+        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
+                nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+                nullable(), nullable(), nullable())
+
+        var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+        assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        assertThat(changedUids).asList().containsExactly(
+                packageSetting1.appId, packageSetting2.appId)
+    }
+
+    @Test
+    fun sendDistractingPackagesChanged_withDifferentVisibilityAllowList() {
+        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+        mockAllowList(packageSetting2, allowList(10001, 10002, 10004))
+
+        distractingPackageHelper.sendDistractingPackagesChanged(pms.snapshotComputer(),
+                packagesToChange, uidsToChange, TEST_USER_ID,
+                PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
+        testHandler.flush()
+        verify(broadcastHelper, times(2)).sendPackageBroadcast(
+                eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(),
+                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
+
+        bundleCaptor.allValues.forEach {
+            var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+            var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+            assertThat(changedPackages?.size).isEqualTo(1)
+            assertThat(changedUids?.size).isEqualTo(1)
+            assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+            assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
+        }
+    }
+
+    @Test
+    fun sendDistractingPackagesChanged_withNullVisibilityAllowList() {
+        mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+        mockAllowList(packageSetting2, null /* list */)
+
+        distractingPackageHelper.sendDistractingPackagesChanged(pms.snapshotComputer(),
+                packagesToChange, uidsToChange, TEST_USER_ID,
+                PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
+        testHandler.flush()
+        verify(broadcastHelper, times(2)).sendPackageBroadcast(
+                eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(),
+                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
+
+        bundleCaptor.allValues.forEach {
+            var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+            var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+            assertThat(changedPackages?.size).isEqualTo(1)
+            assertThat(changedUids?.size).isEqualTo(1)
+            assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+            assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
+        }
+    }
 }
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
index bd012fc..f48d16f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
@@ -20,6 +20,8 @@
 import android.os.Bundle
 import android.os.UserHandle
 import android.os.UserManager
+import android.util.ArrayMap
+import android.util.SparseArray
 import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.testutils.TestHandler
 import com.android.server.testutils.any
@@ -31,6 +33,7 @@
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito
+import org.mockito.Mockito.argThat
 import org.mockito.Mockito.spy
 import org.mockito.MockitoAnnotations
 
@@ -138,4 +141,15 @@
         rule.system().validateFinalState()
         return pms
     }
+
+    protected fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
+        this.put(TEST_USER_ID, uids)
+    }
+
+    protected fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) {
+        whenever(rule.mocks().appsFilter.getVisibilityAllowList(
+                argThat { it?.packageName == pkgSetting.packageName }, any(IntArray::class.java),
+                any() as ArrayMap<String, out PackageStateInternal>
+        )).thenReturn(list)
+    }
 }
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 3ba9ca5..121cbe3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -20,19 +20,14 @@
 import android.content.pm.SuspendDialogInfo
 import android.os.Binder
 import android.os.PersistableBundle
-import android.util.ArrayMap
-import android.util.SparseArray
-import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.testutils.any
 import com.android.server.testutils.eq
 import com.android.server.testutils.nullable
-import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mockito.argThat
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 
@@ -382,16 +377,4 @@
         assertThat(modifiedUids).asList().containsExactly(
                 packageSetting1.appId, packageSetting2.appId)
     }
-
-    private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
-        this.put(TEST_USER_ID, uids)
-    }
-
-    private fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) {
-        whenever(rule.mocks().appsFilter.getVisibilityAllowList(
-            argThat { it?.packageName == pkgSetting.packageName }, any(IntArray::class.java),
-            any() as ArrayMap<String, out PackageStateInternal>
-        ))
-            .thenReturn(list)
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index d5c5745..f28ad79 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -37,8 +37,8 @@
 import android.accounts.CantAddAccountActivity;
 import android.accounts.IAccountManagerResponse;
 import android.app.AppOpsManager;
-import android.app.PropertyInvalidatedCache;
 import android.app.INotificationManager;
+import android.app.PropertyInvalidatedCache;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
@@ -253,6 +253,26 @@
     }
 
     @SmallTest
+    public void testCheckAddAccountLongName() throws Exception {
+        unlockSystemUser();
+        String longString = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                + "aaaaa";
+        Account a11 = new Account(longString, AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+
+        mAms.addAccountExplicitly(
+                a11, /* password= */ "p11", /* extras= */ null, /* callerPackage= */ null);
+
+        String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
+        when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
+        Account[] accounts = mAms.getAccountsAsUser(null,
+                UserHandle.getCallingUserId(), mContext.getOpPackageName());
+        assertEquals(0, accounts.length);
+    }
+
+
+    @SmallTest
     public void testPasswords() throws Exception {
         unlockSystemUser();
         Account a11 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 8c21a39..16406bc 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -129,7 +129,7 @@
         thread2.assertWaiting("Unexpected state for " + record2);
         thread2.interrupt();
 
-        mAms.mProcessList.mActiveUids.clear();
+        clearActiveUids();
     }
 
     private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
@@ -137,11 +137,21 @@
         final UidRecord record = new UidRecord(uid, mAms);
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         record.curProcStateSeq = curProcStateSeq;
-        record.waitingForNetwork = true;
-        mAms.mProcessList.mActiveUids.put(uid, record);
+        record.procStateSeqWaitingForNetwork = 1;
+        addActiveUidRecord(uid, record);
         return record;
     }
 
+    @SuppressWarnings("GuardedBy")
+    private void addActiveUidRecord(int uid, UidRecord record) {
+        mAms.mProcessList.mActiveUids.put(uid, record);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private void clearActiveUids() {
+        mAms.mProcessList.mActiveUids.clear();
+    }
+
     static class CustomThread extends Thread {
         private static final long WAIT_TIMEOUT_MS = 1000;
         private static final long WAIT_INTERVAL_MS = 100;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 36c37c4..c76964e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -196,8 +196,6 @@
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_TOP, // prevState
                 PROCESS_STATE_TOP, // curState
-                0, // expectedGlobalCounter
-                0, // exptectedCurProcStateSeq
                 NETWORK_STATE_NO_CHANGE, // expectedBlockState
                 false); // expectNotify
 
@@ -205,8 +203,6 @@
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_FOREGROUND_SERVICE, // prevState
                 PROCESS_STATE_SERVICE, // curState
-                1, // expectedGlobalCounter
-                1, // exptectedCurProcStateSeq
                 NETWORK_STATE_UNBLOCK, // expectedBlockState
                 true); // expectNotify
 
@@ -218,8 +214,6 @@
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_TRANSIENT_BACKGROUND, // prevState
                 PROCESS_STATE_IMPORTANT_BACKGROUND, // curState
-                42, // expectedGlobalCounter
-                1, // exptectedCurProcStateSeq
                 NETWORK_STATE_NO_CHANGE, // expectedBlockState
                 false); // expectNotify
 
@@ -227,73 +221,22 @@
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_LAST_ACTIVITY, // prevState
                 PROCESS_STATE_TOP, // curState
-                43, // expectedGlobalCounter
-                43, // exptectedCurProcStateSeq
                 NETWORK_STATE_BLOCK, // expectedBlockState
                 false); // expectNotify
 
         // verify waiting threads are not notified.
-        uidRec.waitingForNetwork = false;
+        uidRec.procStateSeqWaitingForNetwork = 0;
         // Uid state is moving from foreground to background.
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_FOREGROUND_SERVICE, // prevState
                 PROCESS_STATE_SERVICE, // curState
-                44, // expectedGlobalCounter
-                44, // exptectedCurProcStateSeq
                 NETWORK_STATE_UNBLOCK, // expectedBlockState
                 false); // expectNotify
-
-        // Verify when uid is not restricted, procStateSeq is not incremented.
-        uidRec.waitingForNetwork = true;
-        mInjector.setNetworkRestrictedForUid(false);
-        verifySeqCounterAndInteractions(uidRec,
-                PROCESS_STATE_IMPORTANT_BACKGROUND, // prevState
-                PROCESS_STATE_TOP, // curState
-                44, // expectedGlobalCounter
-                44, // exptectedCurProcStateSeq
-                -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
-                false); // expectNotify
-
-        // Verify when waitForNetworkTimeout is 0, then procStateSeq is not incremented.
-        mAms.mWaitForNetworkTimeoutMs = 0;
-        mInjector.setNetworkRestrictedForUid(true);
-        verifySeqCounterAndInteractions(uidRec,
-                PROCESS_STATE_TOP, // prevState
-                PROCESS_STATE_IMPORTANT_BACKGROUND, // curState
-                44, // expectedGlobalCounter
-                44, // exptectedCurProcStateSeq
-                -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
-                false); // expectNotify
-
-        // Verify when the uid doesn't have internet permission, then procStateSeq is not
-        // incremented.
-        uidRec.hasInternetPermission = false;
-        mAms.mWaitForNetworkTimeoutMs = 111;
-        mInjector.setNetworkRestrictedForUid(true);
-        verifySeqCounterAndInteractions(uidRec,
-                PROCESS_STATE_CACHED_ACTIVITY, // prevState
-                PROCESS_STATE_FOREGROUND_SERVICE, // curState
-                44, // expectedGlobalCounter
-                44, // exptectedCurProcStateSeq
-                -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
-                false); // expectNotify
-
-        // Verify procStateSeq is not incremented when the uid is not an application, regardless
-        // of the process state.
-        final int notAppUid = 111;
-        final UidRecord uidRec2 = addUidRecord(notAppUid);
-        verifySeqCounterAndInteractions(uidRec2,
-                PROCESS_STATE_CACHED_EMPTY, // prevState
-                PROCESS_STATE_TOP, // curState
-                44, // expectedGlobalCounter
-                0, // exptectedCurProcStateSeq
-                -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
-                false); // expectNotify
     }
 
     private UidRecord addUidRecord(int uid) {
         final UidRecord uidRec = new UidRecord(uid, mAms);
-        uidRec.waitingForNetwork = true;
+        uidRec.procStateSeqWaitingForNetwork = 1;
         uidRec.hasInternetPermission = true;
         mAms.mProcessList.mActiveUids.put(uid, uidRec);
 
@@ -310,18 +253,26 @@
 
     @SuppressWarnings("GuardedBy")
     private void verifySeqCounterAndInteractions(UidRecord uidRec, int prevState, int curState,
-            int expectedGlobalCounter, int expectedCurProcStateSeq, int expectedBlockState,
-            boolean expectNotify) throws Exception {
+            int expectedBlockState, boolean expectNotify) throws Exception {
         CustomThread thread = new CustomThread(uidRec.networkStateLock);
         thread.startAndWait("Unexpected state for " + uidRec);
 
         uidRec.setSetProcState(prevState);
         uidRec.setCurProcState(curState);
+        final long beforeProcStateSeq = mAms.mProcessList.mProcStateSeqCounter;
+
         mAms.mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(mAms.mProcessList.mActiveUids);
 
-        // @SuppressWarnings("GuardedBy")
-        assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
-        assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
+        final long afterProcStateSeq = beforeProcStateSeq
+                + mAms.mProcessList.mActiveUids.size();
+        assertEquals("beforeProcStateSeq=" + beforeProcStateSeq
+                        + ",activeUids.size=" + mAms.mProcessList.mActiveUids.size(),
+                afterProcStateSeq, mAms.mProcessList.mProcStateSeqCounter);
+        assertTrue("beforeProcStateSeq=" + beforeProcStateSeq
+                        + ",afterProcStateSeq=" + afterProcStateSeq
+                        + ",uidCurProcStateSeq=" + uidRec.curProcStateSeq,
+                uidRec.curProcStateSeq > beforeProcStateSeq
+                        && uidRec.curProcStateSeq <= afterProcStateSeq);
 
         for (int i = mAms.mProcessList.getLruSizeLOSP() - 1; i >= 0; --i) {
             final ProcessRecord app = mAms.mProcessList.getLruProcessesLOSP().get(i);
@@ -820,48 +771,11 @@
     }
 
     @Test
-    public void testEnqueueUidChangeLocked_procStateSeqUpdated() {
-        final UidRecord uidRecord = new UidRecord(TEST_UID, mAms);
-        uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
-
-        // Verify with no pending changes for TEST_UID.
-        verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ1);
-
-        // Add a pending change for TEST_UID and verify enqueueUidChangeLocked still works as
-        // expected.
-        uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ2;
-        verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ2);
-    }
-
-    @Test
     public void testEnqueueUidChangeLocked_nullUidRecord() {
         // Use "null" uidRecord to make sure there is no crash.
         mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
     }
 
-    private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) {
-        // Test enqueueUidChangeLocked with every UidRecord.CHANGE_*
-        for (int i = 0; i < UID_RECORD_CHANGES.length; ++i) {
-            final int changeToDispatch = UID_RECORD_CHANGES[i];
-            // Reset lastProcStateSeqDispatchToObservers after every test.
-            uidRecord.lastDispatchedProcStateSeq = 0;
-            mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
-            // Verify there is no effect on curProcStateSeq.
-            assertEquals(curProcstateSeq, uidRecord.curProcStateSeq);
-            if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) {
-                // Since the change is CHANGE_GONE or CHANGE_GONE_IDLE, verify that
-                // lastProcStateSeqDispatchedToObservers is not updated.
-                assertNotEquals(uidRecord.curProcStateSeq,
-                        uidRecord.lastDispatchedProcStateSeq);
-            } else {
-                // Since the change is neither CHANGE_GONE nor CHANGE_GONE_IDLE, verify that
-                // lastProcStateSeqDispatchedToObservers has been updated to curProcStateSeq.
-                assertEquals(uidRecord.curProcStateSeq,
-                        uidRecord.lastDispatchedProcStateSeq);
-            }
-        }
-    }
-
     @MediumTest
     @Test
     public void testEnqueueUidChangeLocked_dispatchUidsChanged() {
@@ -898,29 +812,10 @@
         // Check there is no crash when there is no UidRecord for myUid
         mAms.waitForNetworkStateUpdate(TEST_PROC_STATE_SEQ1);
 
-        // Verify there is no waiting when UidRecord.curProcStateSeq is greater than
-        // the procStateSeq in the request to wait.
-        verifyWaitingForNetworkStateUpdate(
-                TEST_PROC_STATE_SEQ1, // curProcStateSeq
-                TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
-                TEST_PROC_STATE_SEQ1 - 4, // lastNetworkUpdatedProcStateSeq
-                TEST_PROC_STATE_SEQ1 - 2, // procStateSeqToWait
-                false); // expectWait
-
-        // Verify there is no waiting when the procStateSeq in the request to wait is
-        // not dispatched to NPMS.
-        verifyWaitingForNetworkStateUpdate(
-                TEST_PROC_STATE_SEQ1, // curProcStateSeq
-                TEST_PROC_STATE_SEQ1 - 1, // lastDsipatchedProcStateSeq
-                TEST_PROC_STATE_SEQ1 - 1, // lastNetworkUpdatedProcStateSeq
-                TEST_PROC_STATE_SEQ1, // procStateSeqToWait
-                false); // expectWait
-
         // Verify there is not waiting when the procStateSeq in the request already has
         // an updated network state.
         verifyWaitingForNetworkStateUpdate(
                 TEST_PROC_STATE_SEQ1, // curProcStateSeq
-                TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
                 TEST_PROC_STATE_SEQ1, // lastNetworkUpdatedProcStateSeq
                 TEST_PROC_STATE_SEQ1, // procStateSeqToWait
                 false); // expectWait
@@ -928,18 +823,16 @@
         // Verify waiting for network works
         verifyWaitingForNetworkStateUpdate(
                 TEST_PROC_STATE_SEQ1, // curProcStateSeq
-                TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
                 TEST_PROC_STATE_SEQ1 - 1, // lastNetworkUpdatedProcStateSeq
                 TEST_PROC_STATE_SEQ1, // procStateSeqToWait
                 true); // expectWait
     }
 
     private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
-            long lastDispatchedProcStateSeq, long lastNetworkUpdatedProcStateSeq,
+            long lastNetworkUpdatedProcStateSeq,
             final long procStateSeqToWait, boolean expectWait) throws Exception {
         final UidRecord record = new UidRecord(Process.myUid(), mAms);
         record.curProcStateSeq = curProcStateSeq;
-        record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         mAms.mProcessList.mActiveUids.put(Process.myUid(), record);
 
@@ -958,7 +851,7 @@
             }
             thread.assertTerminated(errMsg);
             assertTrue(thread.mNotified);
-            assertFalse(record.waitingForNetwork);
+            assertEquals(0, record.procStateSeqWaitingForNetwork);
         } else {
             thread.start();
             thread.assertTerminated(errMsg);
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
new file mode 100644
index 0000000..049c745
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.restore;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.backup.BackupAgent;
+import android.platform.test.annotations.Presubmit;
+import android.system.OsConstants;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.FileMetadata;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class FullRestoreEngineTest {
+    private static final String DEFAULT_PACKAGE_NAME = "package";
+    private static final String DEFAULT_DOMAIN_NAME = "domain";
+    private static final String NEW_PACKAGE_NAME = "new_package";
+    private static final String NEW_DOMAIN_NAME = "new_domain";
+
+    private FullRestoreEngine mRestoreEngine;
+
+    @Before
+    public void setUp() {
+        mRestoreEngine = new FullRestoreEngine();
+    }
+
+    @Test
+    public void shouldSkipReadOnlyDir_skipsAllReadonlyDirsAndTheirChildren() {
+        // Create the file tree.
+        TestFile[] testFiles = new TestFile[] {
+                TestFile.dir("root"),
+                TestFile.file("root/auth_token"),
+                TestFile.dir("root/media"),
+                TestFile.file("root/media/picture1.png"),
+                TestFile.file("root/push_token.txt"),
+                TestFile.dir("root/read-only-dir-1").markReadOnly().expectSkipped(),
+                TestFile.dir("root/read-only-dir-1/writable-subdir").expectSkipped(),
+                TestFile.file("root/read-only-dir-1/writable-subdir/writable-file").expectSkipped(),
+                TestFile.dir("root/read-only-dir-1/writable-subdir/read-only-subdir-2")
+                        .markReadOnly().expectSkipped(),
+                TestFile.file("root/read-only-dir-1/writable-file").expectSkipped(),
+                TestFile.file("root/random-stuff.txt"),
+                TestFile.dir("root/database"),
+                TestFile.file("root/database/users.db"),
+                TestFile.dir("root/read-only-dir-2").markReadOnly().expectSkipped(),
+                TestFile.file("root/read-only-dir-2/writable-file-1").expectSkipped(),
+                TestFile.file("root/read-only-dir-2/writable-file-2").expectSkipped(),
+        };
+
+        assertCorrectItemsAreSkipped(testFiles);
+    }
+
+    @Test
+    public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSamePackage() {
+        TestFile[] testFiles = new TestFile[]{
+                TestFile.dir("read-only-dir").markReadOnly().expectSkipped(),
+                TestFile.file("read-only-dir/file").expectSkipped(),
+                TestFile.file("read-only-dir/file-from-different-package")
+                        .setPackage(NEW_PACKAGE_NAME),
+        };
+
+        assertCorrectItemsAreSkipped(testFiles);
+    }
+
+    @Test
+    public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSameDomain() {
+        TestFile[] testFiles = new TestFile[]{
+                TestFile.dir("read-only-dir").markReadOnly().expectSkipped(),
+                TestFile.file("read-only-dir/file").expectSkipped(),
+                TestFile.file("read-only-dir/file-from-different-domain")
+                        .setDomain(NEW_DOMAIN_NAME),
+        };
+
+        assertCorrectItemsAreSkipped(testFiles);
+    }
+
+    private void assertCorrectItemsAreSkipped(TestFile[] testFiles) {
+        // Verify all directories marked with .expectSkipped are skipped.
+        for (TestFile testFile : testFiles) {
+            boolean actualExcluded = mRestoreEngine.shouldSkipReadOnlyDir(testFile.mMetadata);
+            boolean expectedExcluded = testFile.mShouldSkip;
+            assertWithMessage(testFile.mMetadata.path).that(actualExcluded).isEqualTo(
+                    expectedExcluded);
+        }
+    }
+
+    private static class TestFile {
+        private final FileMetadata mMetadata;
+        private boolean mShouldSkip;
+
+        static TestFile dir(String path) {
+            return new TestFile(path, BackupAgent.TYPE_DIRECTORY);
+        }
+
+        static TestFile file(String path) {
+            return new TestFile(path, BackupAgent.TYPE_FILE);
+        }
+
+        TestFile markReadOnly() {
+            mMetadata.mode = 0;
+            return this;
+        }
+
+        TestFile expectSkipped() {
+            mShouldSkip = true;
+            return this;
+        }
+
+        TestFile setPackage(String packageName) {
+            mMetadata.packageName = packageName;
+            return this;
+        }
+
+        TestFile setDomain(String domain) {
+            mMetadata.domain = domain;
+            return this;
+        }
+
+        private TestFile(String path, int type) {
+            FileMetadata metadata = new FileMetadata();
+            metadata.path = path;
+            metadata.type = type;
+            metadata.packageName = DEFAULT_PACKAGE_NAME;
+            metadata.domain = DEFAULT_DOMAIN_NAME;
+            metadata.mode = OsConstants.S_IWUSR; // Mark as writable.
+            mMetadata = metadata;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index fe3034d..ce7b367 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -243,6 +243,7 @@
         assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
+    @FlakyTest(bugId = 223153452)
     @Test
     public void registerCallback() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 484b5a8..0f6addb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -982,7 +982,11 @@
     }
 
     @Test
-    public void handleOnInitializeCecComplete_ByScreenOn() {
+    public void handleOnInitializeCecComplete_ByScreenOn_PowerControlModeTv() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                HdmiControlManager.POWER_CONTROL_MODE_TV);
+
         mHdmiCecLocalDevicePlayback.onInitializeCecComplete(
                 mHdmiControlService.INITIATED_BY_SCREEN_ON);
         mTestLooper.dispatchAll();
@@ -999,6 +1003,27 @@
     }
 
     @Test
+    public void handleOnInitializeCecComplete_ByScreenOn_PowerControlModeNone() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                HdmiControlManager.POWER_CONTROL_MODE_NONE);
+
+        mHdmiCecLocalDevicePlayback.onInitializeCecComplete(
+                mHdmiControlService.INITIATED_BY_SCREEN_ON);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSource =
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress);
+        HdmiCecMessage textViewOn =
+                HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+                        ADDR_TV);
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+    }
+
+    @Test
     public void handleOnInitializeCecComplete_ByWakeUpMessage() {
         mHdmiCecLocalDevicePlayback.onInitializeCecComplete(
                 mHdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 6c7f872..0f2fe44 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -991,19 +991,20 @@
     @Test
     public void testUidForeground() throws Exception {
         // push all uids into background
-        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, 0);
-        callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE, 0);
+        long procStateSeq = 0;
+        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
+        callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
         assertFalse(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // push one of the uids into foreground
-        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP, 0);
+        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP, procStateSeq++);
         assertTrue(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // and swap another uid into foreground
-        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, 0);
-        callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP, 0);
+        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
+        callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP, procStateSeq++);
         assertFalse(mService.isUidForeground(UID_A));
         assertTrue(mService.isUidForeground(UID_B));
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index c7a903b..11acfb7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -386,7 +386,8 @@
                 ApexManager.MATCH_ACTIVE_PACKAGE);
         assertThat(newInfo.applicationInfo.sourceDir).isEqualTo(finalApex.getAbsolutePath());
         assertThat(newInfo.applicationInfo.longVersionCode).isEqualTo(2);
-        assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0);
+        assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
+                .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
         assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
             .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
 
@@ -396,7 +397,8 @@
         assertThat(factoryInfo.applicationInfo.longVersionCode).isEqualTo(1);
         assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
             .isEqualTo(ApplicationInfo.FLAG_SYSTEM);
-        assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0);
+        assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
+                .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
     }
 
     @Test
@@ -424,7 +426,8 @@
                 ApexManager.MATCH_ACTIVE_PACKAGE);
         assertThat(newInfo.applicationInfo.sourceDir).isEqualTo(finalApex.getAbsolutePath());
         assertThat(newInfo.applicationInfo.longVersionCode).isEqualTo(2);
-        assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0);
+        assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
+                .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
         assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
             .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
 
@@ -434,7 +437,8 @@
         assertThat(factoryInfo.applicationInfo.longVersionCode).isEqualTo(1);
         assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
             .isEqualTo(ApplicationInfo.FLAG_SYSTEM);
-        assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0);
+        assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
+                .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index a79a52c..b91ff6b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -266,6 +266,11 @@
         public void sendIntentSender(IntentSender intent) {
             // Placeholder for spying.
         }
+
+        @Override
+        public String getPackageName() {
+            return SYSTEM_PACKAGE_NAME;
+        }
     }
 
     /** ShortcutService with injection override methods. */
@@ -698,6 +703,7 @@
 
     protected UriPermissionOwner mUriPermissionOwner;
 
+    protected static final String SYSTEM_PACKAGE_NAME = "android";
 
     protected static final String CALLING_PACKAGE_1 = "com.android.test.1";
     protected static final int CALLING_UID_1 = 10001;
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 07cca0c..4d1c58d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -610,7 +610,7 @@
         assertNotNull(pi.signingInfo);
         assertTrue(pi.signingInfo.getApkContentsSigners().length > 0);
         assertTrue(pi.isApex);
-        assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0);
+        assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
         assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 3e81d60..37792c5 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -87,7 +87,6 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.power.PowerManagerService.BatteryReceiver;
 import com.android.server.power.PowerManagerService.BinderService;
-import com.android.server.power.PowerManagerService.DockReceiver;
 import com.android.server.power.PowerManagerService.Injector;
 import com.android.server.power.PowerManagerService.NativeWrapper;
 import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
@@ -151,7 +150,6 @@
     private Resources mResourcesSpy;
     private OffsettableClock mClock;
     private TestLooper mTestLooper;
-    private DockReceiver mDockReceiver;
 
     private static class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
         private final IntentFilter mFilter;
@@ -338,14 +336,6 @@
                 argThat(new IntentFilterMatcher(usFilter)), isNull(), isA(Handler.class));
         mUserSwitchedReceiver = userSwitchedCaptor.getValue();
 
-        // Grab the DockReceiver
-        ArgumentCaptor<DockReceiver> dockReceiverCaptor =
-                ArgumentCaptor.forClass(DockReceiver.class);
-        IntentFilter dockFilter = new IntentFilter(Intent.ACTION_DOCK_EVENT);
-        verify(mContextSpy).registerReceiver(dockReceiverCaptor.capture(),
-                argThat(new IntentFilterMatcher(dockFilter)), isNull(), isA(Handler.class));
-        mDockReceiver = dockReceiverCaptor.getValue();
-
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
     }
 
@@ -394,16 +384,6 @@
                 .thenReturn(minimumScreenOffTimeoutConfigMillis);
     }
 
-    private void setScreenOffTimeout(int screenOffTimeoutMillis) {
-        Settings.System.putInt(mContextSpy.getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT,
-                screenOffTimeoutMillis);
-    }
-
-    private void setScreenOffTimeoutDocked(int screenOffTimeoutMillis) {
-        Settings.System.putInt(mContextSpy.getContentResolver(),
-                Settings.System.SCREEN_OFF_TIMEOUT_DOCKED, screenOffTimeoutMillis);
-    }
-
     private void advanceTime(long timeMs) {
         mClock.fastForward(timeMs);
         mTestLooper.dispatchAll();
@@ -931,92 +911,6 @@
     }
 
     @Test
-    public void testInattentiveSleep_dreamEnds_goesToSleepAfterTimeout() {
-        setMinimumScreenOffTimeoutConfig(5);
-        setAttentiveTimeout(30000);
-        createService();
-        startSystem();
-
-        advanceTime(10000);
-        forceDream();
-        advanceTime(10000);
-        final String pkg = mContextSpy.getOpPackageName();
-        mService.getBinderServiceInstance().wakeUp(mClock.now(),
-                PowerManager.WAKE_REASON_DREAM_FINISHED, "PowerManagerServiceTest:DREAM_FINISHED",
-                pkg);
-        advanceTime(10001);
-
-        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
-        assertThat(mService.getBinderServiceInstance().getLastSleepReason()).isEqualTo(
-                PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE);
-    }
-
-    @Test
-    public void testScreenOffTimeout_goesToSleepAfterTimeout() {
-        final DisplayInfo info = new DisplayInfo();
-        info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
-        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
-
-        setMinimumScreenOffTimeoutConfig(10);
-        setScreenOffTimeout(10);
-
-        createService();
-        startSystem();
-
-        mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
-                PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
-        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
-        advanceTime(15);
-        assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_AWAKE);
-    }
-
-    @Test
-    public void testScreenOffTimeout_usesRegularTimeoutWhenNotDocked() {
-        final DisplayInfo info = new DisplayInfo();
-        info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
-        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
-
-        setMinimumScreenOffTimeoutConfig(10);
-        setScreenOffTimeout(10);
-        setScreenOffTimeoutDocked(30);
-
-        createService();
-        startSystem();
-
-        mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
-                PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
-        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
-        advanceTime(15);
-        assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_AWAKE);
-    }
-
-    @Test
-    public void testScreenOffTimeout_usesDockedTimeoutWhenDocked() {
-        final DisplayInfo info = new DisplayInfo();
-        info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
-        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
-
-        setMinimumScreenOffTimeoutConfig(10);
-        setScreenOffTimeout(10);
-        setScreenOffTimeoutDocked(30);
-
-        createService();
-        startSystem();
-
-        mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
-                PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
-        mDockReceiver.onReceive(mContextSpy,
-                new Intent(Intent.ACTION_DOCK_EVENT).putExtra(Intent.EXTRA_DOCK_STATE,
-                        Intent.EXTRA_DOCK_STATE_DESK));
-
-        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
-        advanceTime(15);
-        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
-        advanceTime(20);
-        assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_AWAKE);
-    }
-
-    @Test
     public void testInattentiveSleep_goesToSleepWithWakeLock() {
         final String pkg = mContextSpy.getOpPackageName();
         final Binder token = new Binder();
@@ -1039,6 +933,27 @@
     }
 
     @Test
+    public void testInattentiveSleep_dreamEnds_goesToSleepAfterTimeout() {
+        setMinimumScreenOffTimeoutConfig(5);
+        setAttentiveTimeout(30000);
+        createService();
+        startSystem();
+
+        advanceTime(10000);
+        forceDream();
+        advanceTime(10000);
+        final String pkg = mContextSpy.getOpPackageName();
+        mService.getBinderServiceInstance().wakeUp(mClock.now(),
+                PowerManager.WAKE_REASON_DREAM_FINISHED, "PowerManagerServiceTest:DREAM_FINISHED",
+                pkg);
+        advanceTime(10001);
+
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+        assertThat(mService.getBinderServiceInstance().getLastSleepReason()).isEqualTo(
+                PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE);
+    }
+
+    @Test
     public void testInattentiveSleep_wakeLockOnAfterRelease_inattentiveSleepTimeoutNotAffected() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index bd7186e..a7d18ee 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -126,7 +126,7 @@
             when(file.getAbsolutePath()).thenReturn(String.valueOf(i));
             AtomicFile af = new AtomicFile(file);
             expectedFiles.add(af);
-            mDataBase.mHistoryFiles.addLast(af);
+            mDataBase.mHistoryFiles.add(af);
         }
 
         cal.add(Calendar.DATE, -1 * retainDays);
@@ -136,7 +136,7 @@
             when(file.getName()).thenReturn(String.valueOf(cal.getTimeInMillis() - i));
             when(file.getAbsolutePath()).thenReturn(String.valueOf(cal.getTimeInMillis() - i));
             AtomicFile af = new AtomicFile(file);
-            mDataBase.mHistoryFiles.addLast(af);
+            mDataBase.mHistoryFiles.add(af);
         }
 
         // back to today; trim everything a day + old
@@ -162,7 +162,7 @@
             when(file.getName()).thenReturn(i + ".bak");
             when(file.getAbsolutePath()).thenReturn(i + ".bak");
             AtomicFile af = new AtomicFile(file);
-            mDataBase.mHistoryFiles.addLast(af);
+            mDataBase.mHistoryFiles.add(af);
         }
 
         // trim everything a day+ old
@@ -224,7 +224,7 @@
     public void testReadNotificationHistory_readsAllFiles() throws Exception {
         for (long i = 10; i >= 5; i--) {
             AtomicFile af = mock(AtomicFile.class);
-            mDataBase.mHistoryFiles.addLast(af);
+            mDataBase.mHistoryFiles.add(af);
         }
 
         mDataBase.readNotificationHistory();
@@ -248,11 +248,11 @@
     public void testReadNotificationHistory_withNumFilterDoesNotReadExtraFiles() throws Exception {
         AtomicFile af = mock(AtomicFile.class);
         when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
-        mDataBase.mHistoryFiles.addLast(af);
+        mDataBase.mHistoryFiles.add(af);
 
         AtomicFile af2 = mock(AtomicFile.class);
         when(af2.getBaseFile()).thenReturn(new File(mRootDir, "af2"));
-        mDataBase.mHistoryFiles.addLast(af2);
+        mDataBase.mHistoryFiles.add(af2);
 
         mDataBase.readNotificationHistory(null, null, 0);
 
@@ -269,7 +269,7 @@
 
         AtomicFile af = mock(AtomicFile.class);
         when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
-        mDataBase.mHistoryFiles.addLast(af);
+        mDataBase.mHistoryFiles.add(af);
 
         when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(true);
 
@@ -292,7 +292,7 @@
 
         AtomicFile af = mock(AtomicFile.class);
         when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
-        mDataBase.mHistoryFiles.addLast(af);
+        mDataBase.mHistoryFiles.add(af);
 
         when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(false);
 
@@ -315,7 +315,7 @@
 
         AtomicFile af = mock(AtomicFile.class);
         when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
-        mDataBase.mHistoryFiles.addLast(af);
+        mDataBase.mHistoryFiles.add(af);
 
         when(nh.removeConversationsFromWrite("pkg", Set.of("convo", "another"))).thenReturn(true);
 
@@ -338,7 +338,7 @@
 
         AtomicFile af = mock(AtomicFile.class);
         when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
-        mDataBase.mHistoryFiles.addLast(af);
+        mDataBase.mHistoryFiles.add(af);
 
         when(nh.removeConversationsFromWrite("pkg", Set.of("convo"))).thenReturn(false);
 
@@ -361,7 +361,7 @@
 
         AtomicFile af = mock(AtomicFile.class);
         when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
-        mDataBase.mHistoryFiles.addLast(af);
+        mDataBase.mHistoryFiles.add(af);
 
         when(nh.removeChannelFromWrite("pkg", "channel")).thenReturn(true);
 
@@ -384,7 +384,7 @@
 
         AtomicFile af = mock(AtomicFile.class);
         when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
-        mDataBase.mHistoryFiles.addLast(af);
+        mDataBase.mHistoryFiles.add(af);
 
         when(nh.removeChannelFromWrite("pkg", "channel")).thenReturn(false);
 
@@ -424,7 +424,7 @@
         for (int i = 0; i < 5; i++) {
             AtomicFile af = mock(AtomicFile.class);
             when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i));
-            mDataBase.mHistoryFiles.addLast(af);
+            mDataBase.mHistoryFiles.add(af);
         }
         // Baseline size of history files
         assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(5);
@@ -440,7 +440,7 @@
         for (int i = 0; i < 5; i++) {
             AtomicFile af = mock(AtomicFile.class);
             when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i));
-            mDataBase.mHistoryFiles.addLast(af);
+            mDataBase.mHistoryFiles.add(af);
         }
         // Baseline size of history files
         assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(5);
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index dd0c162..a917c57 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -122,8 +122,7 @@
         when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid()))
                 .thenReturn(PERMISSION_GRANTED);
         mService.checkSlicePermission(TEST_URI, mContext.getPackageName(),
-                mContext.getPackageName(), Process.myPid(),
-                Process.myUid(), testPerms);
+                Process.myPid(), Process.myUid(), testPerms);
 
         verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid()));
         verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
@@ -148,7 +147,7 @@
 
     private void grantSlicePermission() {
         doReturn(PERMISSION_GRANTED).when(mService).checkSlicePermission(
-                eq(TEST_URI), anyString(), anyString(), anyInt(), anyInt(), any());
+                eq(TEST_URI), anyString(), anyInt(), anyInt(), any());
         doReturn(PERMISSION_GRANTED).when(mService).checkAccess(
                 anyString(), eq(TEST_URI), anyInt(), anyInt());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 3beb7f2..88c7017 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -283,6 +283,21 @@
         verify(mTransaction).remove(dimLayer);
     }
 
+    @Test
+    public void testDimmerWithBlurUpdatesTransaction() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
+        mHost.addChild(child, 0);
+
+        final int blurRadius = 50;
+        mDimmer.dimBelow(mTransaction, child, 0, blurRadius);
+        SurfaceControl dimLayer = getDimLayer();
+
+        assertNotNull("Dimmer should have created a surface", dimLayer);
+
+        verify(mTransaction).setBackgroundBlurRadius(dimLayer, blurRadius);
+        verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, -1);
+    }
+
     private SurfaceControl getDimLayer() {
         return mDimmer.mDimState.mDimLayer;
     }
diff --git a/services/voiceinteraction/TEST_MAPPING b/services/voiceinteraction/TEST_MAPPING
index 22a6445..bc8c639 100644
--- a/services/voiceinteraction/TEST_MAPPING
+++ b/services/voiceinteraction/TEST_MAPPING
@@ -5,6 +5,19 @@
       "options": [
         {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        // TODO(b/225076204): Remove the following four test cases after fixing the test fail.
+        {
+          "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_createDetectorTwiceQuickly_triggerSuccess"
+        },
+        {
+          "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromExternalSource_success"
+        },
+        {
+          "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_createDetectorTwiceQuickly_triggerSuccess"
+        },
+        {
+          "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromMic_success"
         }
       ]
     },
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 8d4a017..e6c2e04 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -506,6 +506,7 @@
                     if (mValidatingDspTrigger) {
                         mValidatingDspTrigger = false;
                         enforcePermissionsForDataDelivery();
+                        enforceExtraKeyphraseIdNotLeaked(result, recognitionEvent);
                         externalCallback.onKeyphraseDetected(recognitionEvent, result);
                         if (result != null) {
                             Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
@@ -581,6 +582,7 @@
                     mValidatingDspTrigger = false;
                     try {
                         enforcePermissionsForDataDelivery();
+                        enforceExtraKeyphraseIdNotLeaked(result, recognitionEvent);
                     } catch (SecurityException e) {
                         HotwordMetricsLogger.writeKeyphraseTriggerEvent(
                                 mDetectorType,
@@ -1130,6 +1132,19 @@
         }
     }
 
+    private static void enforceExtraKeyphraseIdNotLeaked(HotwordDetectedResult result,
+            SoundTrigger.KeyphraseRecognitionEvent recognitionEvent) {
+        // verify the phrase ID in HotwordDetectedResult is not exposing extra phrases
+        // the DSP did not detect
+        for (SoundTrigger.KeyphraseRecognitionExtra keyphrase : recognitionEvent.keyphraseExtras) {
+            if (keyphrase.getKeyphraseId() == result.getHotwordPhraseId()) {
+                return;
+            }
+        }
+        throw new SecurityException("Ignoring #onDetected due to trusted service "
+                + "sharing a keyphrase ID which the DSP did not detect");
+    }
+
     private static final String OP_MESSAGE =
             "Providing hotword detection result to VoiceInteractionService";
 };
diff --git a/telecomm/java/android/telecom/Logging/EventManager.java b/telecomm/java/android/telecom/Logging/EventManager.java
index 1342038..a74c0bb 100644
--- a/telecomm/java/android/telecom/Logging/EventManager.java
+++ b/telecomm/java/android/telecom/Logging/EventManager.java
@@ -180,7 +180,7 @@
             }
         }
 
-        private final List<Event> mEvents = Collections.synchronizedList(new LinkedList<>());
+        private final List<Event> mEvents = Collections.synchronizedList(new ArrayList<>());
         private final Loggable mRecordEntry;
 
         public EventRecord(Loggable recordEntry) {
@@ -197,7 +197,7 @@
         }
 
         public List<Event> getEvents() {
-            return new LinkedList<>(mEvents);
+            return new ArrayList<>(mEvents);
         }
 
         public List<EventTiming> extractEventTimings() {
@@ -205,7 +205,7 @@
                 return Collections.emptyList();
             }
 
-            LinkedList<EventTiming> result = new LinkedList<>();
+            ArrayList<EventTiming> result = new ArrayList<>();
             Map<String, PendingResponse> pendingResponses = new HashMap<>();
             synchronized (mEvents) {
                 for (Event event : mEvents) {
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index b8ad9e2..ff87ab0 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -359,7 +359,7 @@
         eventTimings = new ArrayList<>();
         in.readTypedList(eventTimings, EventTiming.CREATOR);
         isVideoCall = readByteAsBoolean(in);
-        videoEvents = new LinkedList<>();
+        videoEvents = new ArrayList<>();
         in.readTypedList(videoEvents, VideoEvent.CREATOR);
         callSource = in.readInt();
     }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index e0f5b20..0f7dde5 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1179,7 +1179,7 @@
         if (service != null) {
             try {
                 return service.getSimCallManager(
-                        SubscriptionManager.getDefaultSubscriptionId());
+                        SubscriptionManager.getDefaultSubscriptionId(), mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
             }
@@ -1201,7 +1201,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getSimCallManager(subscriptionId);
+                return service.getSimCallManager(subscriptionId, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
             }
@@ -1225,7 +1225,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getSimCallManagerForUser(userId);
+                return service.getSimCallManagerForUser(userId, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#getSimCallManagerForUser");
             }
@@ -1500,7 +1500,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                service.registerPhoneAccount(account);
+                service.registerPhoneAccount(account, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#registerPhoneAccount", e);
             }
@@ -1516,7 +1516,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                service.unregisterPhoneAccount(accountHandle);
+                service.unregisterPhoneAccount(accountHandle, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#unregisterPhoneAccount", e);
             }
@@ -1597,7 +1597,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getDefaultDialerPackage();
+                return service.getDefaultDialerPackage(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
             }
@@ -1671,7 +1671,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getSystemDialerPackage();
+                return service.getSystemDialerPackage(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException attempting to get the system dialer package name.", e);
             }
@@ -2076,7 +2076,8 @@
                             "acceptHandover for API > O-MR1");
                     return;
                 }
-                service.addNewIncomingCall(phoneAccount, extras == null ? new Bundle() : extras);
+                service.addNewIncomingCall(phoneAccount, extras == null ? new Bundle() : extras,
+                        mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
             }
@@ -2118,7 +2119,8 @@
         if (service != null) {
             try {
                 service.addNewIncomingConference(
-                        phoneAccount, extras == null ? new Bundle() : extras);
+                        phoneAccount, extras == null ? new Bundle() : extras,
+                        mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e);
             }
@@ -2414,7 +2416,7 @@
         Intent result = null;
         if (service != null) {
             try {
-                result = service.createManageBlockedNumbersIntent();
+                result = service.createManageBlockedNumbersIntent(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#createManageBlockedNumbersIntent", e);
             }
@@ -2571,7 +2573,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                service.acceptHandover(srcAddr, videoState, destAcct);
+                service.acceptHandover(srcAddr, videoState, destAcct, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException acceptHandover: " + e);
             }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 07e18d5..b3419e0 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -106,22 +106,22 @@
     /**
      * @see TelecomServiceImpl#getSimCallManager
      */
-    PhoneAccountHandle getSimCallManager(int subId);
+    PhoneAccountHandle getSimCallManager(int subId, String callingPackage);
 
     /**
      * @see TelecomServiceImpl#getSimCallManagerForUser
      */
-    PhoneAccountHandle getSimCallManagerForUser(int userId);
+    PhoneAccountHandle getSimCallManagerForUser(int userId, String callingPackage);
 
     /**
      * @see TelecomServiceImpl#registerPhoneAccount
      */
-    void registerPhoneAccount(in PhoneAccount metadata);
+    void registerPhoneAccount(in PhoneAccount metadata, String callingPackage);
 
     /**
      * @see TelecomServiceImpl#unregisterPhoneAccount
      */
-    void unregisterPhoneAccount(in PhoneAccountHandle account);
+    void unregisterPhoneAccount(in PhoneAccountHandle account, String callingPackage);
 
     /**
      * @see TelecomServiceImpl#clearAccounts
@@ -154,7 +154,7 @@
     /**
      * @see TelecomServiceImpl#getDefaultDialerPackage
      */
-    String getDefaultDialerPackage();
+    String getDefaultDialerPackage(String callingPackage);
 
     /**
      * @see TelecomServiceImpl#getDefaultDialerPackage
@@ -164,7 +164,7 @@
     /**
      * @see TelecomServiceImpl#getSystemDialerPackage
      */
-    String getSystemDialerPackage();
+    String getSystemDialerPackage(String callingPackage);
 
     /**
     * @see TelecomServiceImpl#dumpCallAnalytics
@@ -262,12 +262,15 @@
     /**
      * @see TelecomServiceImpl#addNewIncomingCall
      */
-    void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
+    void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras,
+            String callingPackage);
 
     /**
      * @see TelecomServiceImpl#addNewIncomingConference
      */
-    void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras);
+    void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras,
+            String callingPackage);
+
 
     /**
      * @see TelecomServiceImpl#addNewUnknownCall
@@ -303,7 +306,7 @@
     /**
     * @see TelecomServiceImpl#createManageBlockedNumbersIntent
     **/
-    Intent createManageBlockedNumbersIntent();
+    Intent createManageBlockedNumbersIntent(String callingPackage);
 
    /**
     * @see TelecomServiceImpl#createLaunchEmergencyDialerIntent
@@ -330,7 +333,8 @@
     /**
      * @see TelecomServiceImpl#acceptHandover
      */
-    void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct);
+    void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct,
+                String callingPackage);
 
     /**
      * @see TelecomServiceImpl#setTestEmergencyPhoneAccountPackageNameFilter
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a6a7c84..314104f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -176,7 +176,7 @@
      * This flag specifies whether VoLTE availability is based on provisioning. By default this is
      * false.
      * Used for UCE to determine if EAB provisioning checks should be based on provisioning.
-     * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead.
+     * @deprecated Use {@link Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL} instead.
      */
     @Deprecated
     public static final String
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7b5d62d..7f0df82 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7742,7 +7742,7 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * TODO: remove this one. use {@link #rebootModem()} for reset type 1 and
-     * {@link #resetRadioConfig()} for reset type 3
+     * {@link #resetRadioConfig()} for reset type 3 (b/116476729)
      *
      * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @return true on success; false on any failure.
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 5bae1ad..f8048aa 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -539,7 +539,6 @@
      *     <li>The caller has carrier privileges (see
      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
      *     active subscription.</li>
-     *     <li>The caller is the default SMS app for the device.</li>
      * </ul>
      * <p>The profile owner is an app that owns a managed profile on the device; for more details
      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
@@ -601,7 +600,6 @@
      *     <li>The caller has carrier privileges (see
      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
      *     active subscription.</li>
-     *     <li>The caller is the default SMS app for the device.</li>
      * </ul>
      * <p>The profile owner is an app that owns a managed profile on the device; for more details
      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
@@ -649,7 +647,6 @@
      *     <li>The caller has carrier privileges (see
      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
      *     active subscription.</li>
-     *     <li>The caller is the default SMS app for the device.</li>
      * </ul>
      * <p>The profile owner is an app that owns a managed profile on the device; for more details
      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
@@ -862,7 +859,6 @@
      *     <li>The caller has carrier privileges (see
      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
      *     active subscription.</li>
-     *     <li>The caller is the default SMS app for the device.</li>
      * </ul>
      * <p>The profile owner is an app that owns a managed profile on the device; for more details
      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
@@ -937,7 +933,6 @@
      *     <li>The caller has carrier privileges (see
      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
      *     active subscription.</li>
-     *     <li>The caller is the default SMS app for the device.</li>
      * </ul>
      * <p>The profile owner is an app that owns a managed profile on the device; for more details
      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
@@ -1111,7 +1106,6 @@
      *     <li>The caller has carrier privileges (see
      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
      *     active subscription.</li>
-     *     <li>The caller is the default SMS app for the device.</li>
      * </ul>
      * <p>The profile owner is an app that owns a managed profile on the device; for more details
      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
@@ -1226,7 +1220,6 @@
      *     <li>The caller has carrier privileges (see
      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
      *     active subscription.</li>
-     *     <li>The caller is the default SMS app for the device.</li>
      * </ul>
      * <p>The profile owner is an app that owns a managed profile on the device; for more details
      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
@@ -1415,7 +1408,6 @@
      *     <li>The caller has carrier privileges (see
      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
      *     active subscription.</li>
-     *     <li>The caller is the default SMS app for the device.</li>
      * </ul>
      * <p>The profile owner is an app that owns a managed profile on the device; for more details
      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 3415868..4439e5c 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -43,6 +44,8 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.ITelephony;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -87,6 +90,46 @@
             "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
 
     /**
+     * This carrier supports User Capability Exchange as, defined by the framework using a
+     * presence server. If set, the RcsFeature should support capability exchange. If not set, this
+     * RcsFeature should not publish capabilities or service capability requests.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
+            CAPABILITY_TYPE_NONE,
+            CAPABILITY_TYPE_OPTIONS_UCE,
+            CAPABILITY_TYPE_PRESENCE_UCE
+    })
+    public @interface RcsImsCapabilityFlag {}
+
+    /**
+     * Undefined capability type for initialization
+     */
+    public static final int CAPABILITY_TYPE_NONE = 0;
+
+    /**
+     * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+     * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+     * If not set, this RcsFeature should not service capability requests.
+     */
+    public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+    /**
+     * This carrier supports User Capability Exchange using a presence server as defined by the
+     * framework. If set, the RcsFeature should support capability exchange using a presence
+     * server. If not set, this RcsFeature should not publish capabilities or service capability
+     * requests using presence.
+     */
+    public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
+
+    /**
+     * This is used to check the upper range of RCS capability
+     * @hide
+     */
+    public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
+
+    /**
      * An application can use {@link #addOnAvailabilityChangedListener} to register a
      * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature
      * availability status updates from the ImsService.
@@ -104,7 +147,7 @@
          *
          * @param capabilities The new availability of the capabilities.
          */
-        void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities);
+        void onAvailabilityChanged(@RcsImsCapabilityFlag int capabilities);
     }
 
     /**
@@ -486,7 +529,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
+    public boolean isCapable(@RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
@@ -497,6 +540,8 @@
 
         try {
             return imsRcsController.isCapable(mSubId, capability, radioTech);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
             Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -522,7 +567,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
+    public boolean isAvailable(@RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)
             throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
@@ -534,6 +579,8 @@
 
         try {
             return imsRcsController.isAvailable(mSubId, capability, radioTech);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
             Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
             throw new ImsException("Remote IMS Service is not available",
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index be233b8..50fcdf8 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -52,6 +52,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
@@ -188,6 +189,7 @@
             new SparseArray<>();
 
     private IImsServiceControllerListener mListener;
+    private final Object mListenerLock = new Object();
     private Executor mExecutor;
 
     /**
@@ -225,7 +227,30 @@
     protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
         @Override
         public void setListener(IImsServiceControllerListener l) {
-            mListener = l;
+            synchronized (mListenerLock) {
+                if (mListener != null && mListener.asBinder().isBinderAlive()) {
+                    try {
+                        mListener.asBinder().unlinkToDeath(mDeathRecipient, 0);
+                    } catch (NoSuchElementException e) {
+                        Log.w(LOG_TAG, "IImsServiceControllerListener does not exist");
+                    }
+                }
+
+                mListener = l;
+                if (mListener == null) {
+                    executeMethodAsync(() -> releaseResource(), "releaseResource");
+                    return;
+                }
+
+                try {
+                    mListener.asBinder().linkToDeath(mDeathRecipient, 0);
+                    Log.i(LOG_TAG, "setListener: register linkToDeath");
+                } catch (RemoteException e) {
+                    // RemoteException means target binder process was crashed
+                    // release resource
+                    executeMethodAsync(() -> releaseResource(), "releaseResource");
+                }
+            }
         }
 
         @Override
@@ -364,28 +389,15 @@
                     ImsService.this.disableImsForSubscription(slotId, subId), "disableIms");
         }
 
-        // Call the methods with a clean calling identity on the executor and wait indefinitely for
-        // the future to return.
-        private void executeMethodAsync(Runnable r, String errorLogName) {
-            try {
-                CompletableFuture.runAsync(
-                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
-            } catch (CancellationException | CompletionException e) {
-                Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
-                        + e.getMessage());
-            }
-        }
 
-        private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
-            CompletableFuture<T> future = CompletableFuture.supplyAsync(
-                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
-            try {
-                return future.get();
-            } catch (ExecutionException | InterruptedException e) {
-                Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
-                        + e.getMessage());
-                return null;
-            }
+    };
+
+    private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            Log.w(LOG_TAG,
+                    "IImsServiceControllerListener binder to framework has died. Cleaning up");
+            executeMethodAsync(() -> releaseResource(), "releaseResource");
         }
     };
 
@@ -490,6 +502,9 @@
     }
 
     private void removeImsFeature(int slotId, int featureType) {
+        // clear cached data
+        notifySubscriptionRemoved(slotId);
+
         synchronized (mFeaturesBySlot) {
             // get ImsFeature associated with the slot/feature
             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
@@ -507,7 +522,6 @@
             f.onFeatureRemoved();
             features.remove(featureType);
         }
-
     }
 
     /**
@@ -552,6 +566,54 @@
         return createFlag;
     }
 
+    private void releaseResource() {
+        Log.w(LOG_TAG, "cleaning up features");
+        synchronized (mFeaturesBySlot) {
+            SparseArray<ImsFeature> features;
+            ImsFeature imsFeature;
+
+            for (int i = 0; i < mFeaturesBySlot.size(); i++) {
+                features = mFeaturesBySlot.valueAt(i);
+                if (features == null) {
+                    continue;
+                }
+
+                for (int index = 0; index < features.size(); index++) {
+                    imsFeature = features.valueAt(index);
+                    if (imsFeature != null) {
+                        imsFeature.onFeatureRemoved();
+                    }
+                }
+                features.clear();
+            }
+            mFeaturesBySlot.clear();
+        }
+    }
+
+    // Call the methods with a clean calling identity on the executor and wait indefinitely for
+    // the future to return.
+    private void executeMethodAsync(Runnable r, String errorLogName) {
+        try {
+            CompletableFuture.runAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+        } catch (CancellationException | CompletionException e) {
+            Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+                    + e.getMessage());
+        }
+    }
+
+    private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
+        CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+                    + e.getMessage());
+            return null;
+        }
+    }
+
     /**
      * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
      * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
@@ -574,10 +636,14 @@
      */
     public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
             throws RemoteException {
-        if (mListener == null) {
-            throw new IllegalStateException("Framework is not ready");
+        IImsServiceControllerListener l;
+        synchronized (mListenerLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Framework is not ready");
+            }
+            l = mListener;
         }
-        mListener.onUpdateSupportedImsFeatures(c);
+        l.onUpdateSupportedImsFeatures(c);
     }
 
     /**
@@ -629,6 +695,24 @@
     }
 
     /**
+     * The subscription has removed. The ImsService should notify ImsRegistrationImplBase and
+     * ImsConfigImplBase the SIM state was changed.
+     * @param slotId The slot ID which has removed.
+     */
+    private void notifySubscriptionRemoved(int slotId) {
+        ImsRegistrationImplBase registrationImplBase =
+                getRegistration(slotId);
+        if (registrationImplBase != null) {
+            registrationImplBase.clearRegistrationCache();
+        }
+
+        ImsConfigImplBase imsConfigImplBase = getConfig(slotId);
+        if (imsConfigImplBase != null) {
+            imsConfigImplBase.clearConfigurationCache();
+        }
+    }
+
+    /**
      * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
      * and perform all appropriate initialization to bring up all ImsFeatures.
      * @deprecated Use {@link #enableImsForSubscription} instead.
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 677c1a9..f2976f1 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -38,7 +38,6 @@
 import android.telephony.ims.aidl.IImsConfigCallback;
 import android.telephony.ims.aidl.IRcsConfigCallback;
 import android.telephony.ims.feature.MmTelFeature;
-import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 
@@ -55,6 +54,9 @@
  * IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
  * applications and may vary. It is up to the carrier and OEM applications to ensure that the
  * correct provisioning keys are being used when integrating with a vendor's ImsService.
+ *
+ * Use {@link android.telephony.ims.ImsManager#getProvisioningManager(int)} to get an instance of
+ * this manager.
  */
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
 public class ProvisioningManager {
@@ -970,7 +972,7 @@
     /**
      * Callback for IMS provisioning feature changes.
      */
-    public static class FeatureProvisioningCallback {
+    public abstract static class FeatureProvisioningCallback {
 
         private static class CallbackBinder extends IFeatureProvisioningCallback.Stub {
 
@@ -1024,12 +1026,10 @@
          * specified, or {@code false} if the capability is not provisioned for the technology
          * specified.
          */
-        public void onFeatureProvisioningChanged(
+        public abstract void onFeatureProvisioningChanged(
                 @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
                 @ImsRegistrationImplBase.ImsRegistrationTech int tech,
-                boolean isProvisioned) {
-            // Base Implementation
-        }
+                boolean isProvisioned);
 
         /**
          * The IMS RCS provisioning has changed for a specific capability and IMS
@@ -1041,12 +1041,10 @@
          * specified, or {@code false} if the capability is not provisioned for the technology
          * specified.
          */
-        public void onRcsFeatureProvisioningChanged(
-                @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+        public abstract void onRcsFeatureProvisioningChanged(
+                @ImsRcsManager.RcsImsCapabilityFlag int capability,
                 @ImsRegistrationImplBase.ImsRegistrationTech int tech,
-                boolean isProvisioned) {
-            // Base Implementation
-        }
+                boolean isProvisioned);
 
         /**@hide*/
         public final IFeatureProvisioningCallback getBinder() {
@@ -1505,7 +1503,7 @@
      * Get the provisioning status for the IMS RCS capability specified.
      *
      * If provisioning is not required for the queried
-     * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return
+     * {@link ImsRcsManager.RcsImsCapabilityFlag} this method will always return
      * {@code true}.
      *
      * @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
@@ -1522,7 +1520,7 @@
     @WorkerThread
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean getRcsProvisioningStatusForCapability(
-            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+            @ImsRcsManager.RcsImsCapabilityFlag int capability) {
         try {
             return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability,
             ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
@@ -1535,7 +1533,7 @@
      * Get the provisioning status for the IMS RCS capability specified.
      *
      * If provisioning is not required for the queried
-     * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method
+     * {@link ImsRcsManager.RcsImsCapabilityFlag} this method
      * will always return {@code true}.
      *
      * <p> Requires Permission:
@@ -1553,7 +1551,7 @@
     @WorkerThread
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public boolean getRcsProvisioningStatusForCapability(
-            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
         try {
             return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability, tech);
@@ -1590,7 +1588,7 @@
     @WorkerThread
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRcsProvisioningStatusForCapability(
-            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
             boolean isProvisioned) {
         try {
             getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
@@ -1622,7 +1620,7 @@
     @WorkerThread
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRcsProvisioningStatusForCapability(
-            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
         try {
             getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
@@ -1676,7 +1674,7 @@
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public boolean isRcsProvisioningRequiredForCapability(
-            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
         try {
             return getITelephony().isRcsProvisioningRequiredForCapability(mSubId, capability, tech);
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 154bb11..91dc38f 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -55,20 +55,28 @@
      * This carrier supports User Capability Exchange as, defined by the framework using
      * SIP OPTIONS. If set, the RcsFeature should support capability exchange. If not set, this
      * RcsFeature should not publish capabilities or service capability requests.
+     * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_OPTIONS_UCE} instead.
      * @hide
      */
+    @Deprecated
     public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
 
     /**
      * This carrier supports User Capability Exchange as, defined by the framework using a
      * presence server. If set, the RcsFeature should support capability exchange. If not set, this
      * RcsFeature should not publish capabilities or service capability requests.
+     * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_PRESENCE_UCE} instead.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
 
-    /**@hide*/
+    /**
+     * @deprecated Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead.
+     * @hide
+     */
+    @Deprecated
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "CAPABILITY_TYPE_", value = {
             CAPABILITY_TYPE_OPTIONS_UCE,
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index ad2e9e1..fb0e659 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -396,7 +396,7 @@
         /**
          * Undefined capability type for initialization
          * This is used to check the upper range of MmTel capability
-         * {@hide}
+         * @hide
          */
         public static final int CAPABILITY_TYPE_NONE = 0;
 
@@ -427,7 +427,7 @@
 
         /**
          * This is used to check the upper range of MmTel capability
-         * {@hide}
+         * @hide
          */
         public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1;
 
@@ -738,7 +738,7 @@
      * Enabling/Disabling a capability here indicates that the capability should be registered or
      * deregistered (depending on the capability change) and become available or unavailable to
      * the framework.
-     * * @hide
+     * @hide
      */
     @Override
     @SystemApi
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 70e4ef1..843827b 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -24,7 +24,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.ImsRcsManager;
 import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper;
 import android.telephony.ims.aidl.ICapabilityExchangeEventListener;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -59,7 +59,9 @@
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
  * this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
  */
+@SystemApi
 public class RcsFeature extends ImsFeature {
 
     private static final String LOG_TAG = "RcsFeature";
@@ -184,18 +186,22 @@
      * Contains the capabilities defined and supported by a {@link RcsFeature} in the
      * form of a bitmask. The capabilities that are used in the RcsFeature are
      * defined as:
-     * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
-     * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+     * {@link ImsRcsManager.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+     * {@link ImsRcsManager.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
      *
      * The enabled capabilities of this RcsFeature will be set by the framework
-     * using {#changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+     * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
      * After the capabilities have been set, the RcsFeature may then perform the necessary bring up
      * of the capability and notify the capability status as true using
-     * {#notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
      * framework that the capability is available for usage.
      */
     public static class RcsImsCapabilities extends Capabilities {
-        /** @hide*/
+
+        /**
+         * Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead in case used for public API
+         * @hide
+         */
         @Retention(RetentionPolicy.SOURCE)
         @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
                 CAPABILITY_TYPE_NONE,
@@ -226,7 +232,7 @@
 
         /**
          * This is used to check the upper range of RCS capability
-         * {@hide}
+         * @hide
          */
         public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
 
@@ -234,10 +240,8 @@
          * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
          * @param capabilities The capabilities that are supported for RCS in the form of a
          * bitfield.
-         * @hide
          */
-        @SystemApi
-        public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+        public RcsImsCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
             super(capabilities);
         }
 
@@ -249,30 +253,18 @@
             super(capabilities.getMask());
         }
 
-        /**
-         * @hide
-         */
         @Override
-        @SystemApi
-        public void addCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+        public void addCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
             super.addCapabilities(capabilities);
         }
 
-        /**
-         * @hide
-         */
         @Override
-        @SystemApi
-        public void removeCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+        public void removeCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
             super.removeCapabilities(capabilities);
         }
 
-        /**
-         * @hide
-         */
         @Override
-        @SystemApi
-        public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+        public boolean isCapable(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
             return super.isCapable(capabilities);
         }
     }
@@ -288,9 +280,7 @@
      * Method stubs called from the framework will be called asynchronously. To specify the
      * {@link Executor} that the methods stubs will be called, use
      * {@link RcsFeature#RcsFeature(Executor)} instead.
-     * @hide
      */
-    @SystemApi
     public RcsFeature() {
         super();
         // Run on the Binder threads that call them.
@@ -302,9 +292,7 @@
      * framework.
      * @param executor The executor for the framework to use when executing the methods overridden
      * by the implementation of RcsFeature.
-     * @hide
      */
-    @SystemApi
     public RcsFeature(@NonNull Executor executor) {
         super();
         if (executor == null) {
@@ -335,10 +323,8 @@
      * requests. To change the status of the capabilities
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
      * @return A copy of the current RcsFeature capability status.
-     * @hide
      */
     @Override
-    @SystemApi
     public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
         return new RcsImsCapabilities(super.queryCapabilityStatus());
     }
@@ -348,9 +334,7 @@
      * this signals to the framework that the capability has been initialized and is ready.
      * Call {@link #queryCapabilityStatus()} to return the current capability status.
      * @param capabilities The current capability status of the RcsFeature.
-     * @hide
      */
-    @SystemApi
     public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
         if (capabilities == null) {
             throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
@@ -367,11 +351,9 @@
      * @param capability The capability that we are querying the configuration for.
      * @param radioTech The radio technology type that we are querying.
      * @return true if the capability is enabled, false otherwise.
-     * @hide
      */
-    @SystemApi
     public boolean queryCapabilityConfiguration(
-            @RcsUceAdapter.RcsImsCapabilityFlag int capability,
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         // Base Implementation - Override to provide functionality
         return false;
@@ -392,10 +374,8 @@
      * be called for each capability change that resulted in an error.
      * @param request The request to change the capability.
      * @param callback To notify the framework that the result of the capability changes.
-     * @hide
      */
     @Override
-    @SystemApi
     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
             @NonNull CapabilityCallbackProxy callback) {
         // Base Implementation - Override to provide functionality
@@ -415,9 +395,7 @@
      * event to the framework.
      * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
      * exchange if it is supported by the device.
-     * @hide
      */
-    @SystemApi
     public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
             @NonNull CapabilityExchangeEventListener listener) {
         // Base Implementation, override to implement functionality
@@ -427,28 +405,20 @@
     /**
      * Remove the given CapabilityExchangeImplBase instance.
      * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
-     * @hide
      */
-    @SystemApi
     public void destroyCapabilityExchangeImpl(
             @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
         // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
     }
 
-    /**{@inheritDoc}
-     * @hide
-     */
+    /**{@inheritDoc}*/
     @Override
-    @SystemApi
     public void onFeatureRemoved() {
 
     }
 
-    /**{@inheritDoc}
-     * @hide
-     */
+    /**{@inheritDoc}*/
     @Override
-    @SystemApi
     public void onFeatureReady() {
 
     }
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index f371ec3..897b57f 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -481,6 +481,17 @@
             }
         }
 
+        /**
+         * Clear cached configuration value.
+         */
+        public void clearCachedValue() {
+            Log.i(TAG, "clearCachedValue");
+            synchronized (mLock) {
+                mProvisionedIntValue.clear();
+                mProvisionedStringValue.clear();
+            }
+        }
+
         // Call the methods with a clean calling identity on the executor and wait indefinitely for
         // the future to return.
         private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
@@ -538,6 +549,7 @@
     private final RemoteCallbackListExt<IRcsConfigCallback> mRcsCallbacks =
             new RemoteCallbackListExt<>();
     private byte[] mRcsConfigData;
+    private final Object mRcsConfigDataLock = new Object();
     ImsConfigStub mImsConfigStub;
 
     /**
@@ -616,12 +628,20 @@
 
     private void addRcsConfigCallback(IRcsConfigCallback c) {
         mRcsCallbacks.register(c);
-        if (mRcsConfigData != null) {
-            try {
-                c.onConfigurationChanged(mRcsConfigData);
-            } catch (RemoteException e) {
-                Log.w(TAG, "dead binder to call onConfigurationChanged, skipping.");
+
+        // This is used to avoid calling the binder out of the synchronized scope.
+        byte[] cloneRcsConfigData;
+        synchronized (mRcsConfigDataLock) {
+            if (mRcsConfigData == null) {
+                return;
             }
+            cloneRcsConfigData = mRcsConfigData.clone();
+        }
+
+        try {
+            c.onConfigurationChanged(cloneRcsConfigData);
+        } catch (RemoteException e) {
+            Log.w(TAG, "dead binder to call onConfigurationChanged, skipping.");
         }
     }
 
@@ -631,18 +651,23 @@
 
     private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
         // cache uncompressed config
-        config = isCompressed ? RcsConfig.decompressGzip(config) : config;
-        if (Arrays.equals(mRcsConfigData, config)) {
-            return;
+        final byte[] rcsConfigData = isCompressed ? RcsConfig.decompressGzip(config) : config;
+
+        synchronized (mRcsConfigDataLock) {
+            if (Arrays.equals(mRcsConfigData, config)) {
+                return;
+            }
+            mRcsConfigData = rcsConfigData;
         }
-        mRcsConfigData = config;
 
         // can be null in testing
         if (mRcsCallbacks != null) {
             synchronized (mRcsCallbacks) {
                 mRcsCallbacks.broadcastAction(c -> {
                     try {
-                        c.onConfigurationChanged(mRcsConfigData);
+                        // config is cloned here so modifications to the config passed to the
+                        // vendor do not accidentally modify the cache.
+                        c.onConfigurationChanged(rcsConfigData.clone());
                     } catch (RemoteException e) {
                         Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping.");
                     }
@@ -653,7 +678,9 @@
     }
 
     private void onNotifyRcsAutoConfigurationRemoved() {
-        mRcsConfigData = null;
+        synchronized (mRcsConfigDataLock) {
+            mRcsConfigData = null;
+        }
         if (mRcsCallbacks != null) {
             synchronized (mRcsCallbacks) {
                 mRcsCallbacks.broadcastAction(c -> {
@@ -857,4 +884,17 @@
             mImsConfigStub.mExecutor = executor;
         }
     }
+
+    /**
+     * Clear all cached config data. This will be called when the config data is no longer valid
+     * such as when the SIM was removed.
+     * @hide
+     */
+    public final void clearConfigurationCache() {
+        mImsConfigStub.clearCachedValue();
+
+        synchronized (mRcsConfigDataLock) {
+            mRcsConfigData = null;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index ac5565b..6fc1cc8 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -93,7 +93,7 @@
 
     /**
      * This is used to check the upper range of registration tech
-     * {@hide}
+     * @hide
      */
     public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_NR + 1;
 
@@ -517,4 +517,16 @@
             mExecutor = executor;
         }
     }
+
+    /**
+     * Clear the cached data when the subscription is no longer valid
+     * such as when a sim is removed.
+     * @hide
+     */
+    public final void clearRegistrationCache() {
+        synchronized (mLock) {
+            mUris = null;
+            mUrisSet = false;
+        }
+    }
 }
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 7490d3f..ab83997 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -114,6 +114,63 @@
         Uninstall.packages(TestApp.A, TestApp.B);
     }
 
+    private boolean isSystem(PackageInfo info) {
+        return (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    private boolean isUpdatedSystem(PackageInfo info) {
+        return (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+    }
+
+    @Test
+    public void testUpdateSystemApp_InstallV2() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+
+        PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+        PackageInfo info;
+        // Check factory version
+        info = pm.getPackageInfo(TestApp.A,
+                PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY));
+        assertThat(isSystem(info)).isTrue();
+        assertThat(isUpdatedSystem(info)).isFalse();
+        // Check active version
+        info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0));
+        assertThat(isSystem(info)).isTrue();
+        assertThat(isUpdatedSystem(info)).isFalse();
+
+        Install.single(TestApp.A2).commit();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+        // Check factory version
+        info = pm.getPackageInfo(TestApp.A,
+                PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY));
+        assertThat(isSystem(info)).isTrue();
+        assertThat(isUpdatedSystem(info)).isFalse();
+        // Check active version
+        info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0));
+        assertThat(isSystem(info)).isTrue();
+        assertThat(isUpdatedSystem(info)).isTrue();
+    }
+
+    @Test
+    public void testUpdateSystemApp_PostInstallV2() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+        PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+        PackageInfo info;
+        // Check factory version
+        info = pm.getPackageInfo(TestApp.A,
+                PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY));
+        assertThat(isSystem(info)).isTrue();
+        assertThat(isUpdatedSystem(info)).isFalse();
+        // Check active version
+        info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0));
+        assertThat(isSystem(info)).isTrue();
+        assertThat(isUpdatedSystem(info)).isTrue();
+    }
+
     @Test
     public void testDuplicateApkInApexShouldFail_Commit() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -403,7 +460,8 @@
         {
             PackageInfo apex = pm.getPackageInfo("test.apex.rebootless", PackageManager.MATCH_APEX);
             assertThat(apex.getLongVersionCode()).isEqualTo(1);
-            assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0);
+            assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
+                    .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
             assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
                     .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
             assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active");
@@ -414,7 +472,8 @@
             assertThat(apex.getLongVersionCode()).isEqualTo(1);
             assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
                     .isEqualTo(ApplicationInfo.FLAG_SYSTEM);
-            assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0);
+            assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
+                    .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
             assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex");
         }
 
@@ -425,7 +484,8 @@
         {
             PackageInfo apex = pm.getPackageInfo("test.apex.rebootless", PackageManager.MATCH_APEX);
             assertThat(apex.getLongVersionCode()).isEqualTo(2);
-            assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0);
+            assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
+                    .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
             assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
                     .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
             assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active");
@@ -436,7 +496,8 @@
             assertThat(apex.getLongVersionCode()).isEqualTo(1);
             assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
                     .isEqualTo(ApplicationInfo.FLAG_SYSTEM);
-            assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0);
+            assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
+                    .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
             assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex");
         }
     }
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index f60b4d6..7e0a55f 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -95,6 +95,7 @@
                 "/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex",
                 "/system/apex/test.rebootless_apex_v*.apex",
                 "/data/apex/active/test.apex.rebootless*.apex",
+                "/system/app/TestApp/TestAppAv1.apk",
                 TEST_VENDOR_APEX_ALLOW_LIST);
     }
 
@@ -163,6 +164,25 @@
     }
 
     /**
+     * Tests app info flags are set correctly when updating a system app.
+     */
+    @Test
+    public void testUpdateSystemApp() throws Exception {
+        // Push TestAppAv1.apk to /system
+        final File apkFile = mHostUtils.getTestFile(APK_A);
+        if (!getDevice().isAdbRoot()) {
+            getDevice().enableAdbRoot();
+        }
+        getDevice().remountSystemWritable();
+        assertTrue(getDevice().pushFile(apkFile, "/system/app/TestApp/" + apkFile.getName()));
+        getDevice().reboot();
+
+        runPhase("testUpdateSystemApp_InstallV2");
+        getDevice().reboot();
+        runPhase("testUpdateSystemApp_PostInstallV2");
+    }
+
+    /**
      * Tests that duplicate packages in apk-in-apex and apk should fail to install.
      */
     @Test
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index b7f5b15..f183a9b 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -20,6 +20,7 @@
 import android.service.voice.AlwaysOnHotwordDetector;
 import android.service.voice.AlwaysOnHotwordDetector.Callback;
 import android.service.voice.AlwaysOnHotwordDetector.EventPayload;
+import android.service.voice.HotwordDetector;
 import android.service.voice.VoiceInteractionService;
 import android.util.Log;
 
@@ -83,16 +84,24 @@
                 break;
             case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED:
                 Log.i(TAG, "STATE_KEYPHRASE_UNENROLLED");
-                Intent enroll = mHotwordDetector.createEnrollIntent();
-                Log.i(TAG, "Need to enroll with " + enroll);
+                try {
+                    Intent enroll = mHotwordDetector.createEnrollIntent();
+                    Log.i(TAG, "Need to enroll with " + enroll);
+                } catch (HotwordDetector.IllegalDetectorStateException e) {
+                    Log.e(TAG, "createEnrollIntent failed", e);
+                }
                 break;
             case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED:
                 Log.i(TAG, "STATE_KEYPHRASE_ENROLLED - starting recognition");
-                if (mHotwordDetector.startRecognition(
-                        AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE)) {
-                    Log.i(TAG, "startRecognition succeeded");
-                } else {
-                    Log.i(TAG, "startRecognition failed");
+                try {
+                    if (mHotwordDetector.startRecognition(
+                            AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE)) {
+                        Log.i(TAG, "startRecognition succeeded");
+                    } else {
+                        Log.i(TAG, "startRecognition failed");
+                    }
+                } catch (HotwordDetector.IllegalDetectorStateException e) {
+                    Log.e(TAG, "startRecognition failed", e);
                 }
                 break;
         }
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index bfb3285..0849600 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -36,6 +36,7 @@
 
 cc_defaults {
     name: "aapt2_defaults",
+    cpp_std: "gnu++2b",
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index f47d66e..41896f6 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -273,7 +273,7 @@
     printer->Indent();
     for (const auto& type : package.types) {
       printer->Print("type ");
-      printer->Print(to_string(type.type));
+      printer->Print(type.named_type.to_string());
       if (type.id) {
         printer->Print(StringPrintf(" id=%02x", type.id.value()));
       }
@@ -287,7 +287,7 @@
         printer->Print(" ");
 
         // Write the name without the package (this is obvious and too verbose).
-        printer->Print(to_string(type.type));
+        printer->Print(type.named_type.to_string());
         printer->Print("/");
         printer->Print(entry.name);
 
@@ -547,7 +547,7 @@
           const auto policy_subsection = StringPrintf(R"(policies="%s")",
               android::idmap2::policy::PoliciesToDebugString(overlayable_item.policies).c_str());
           const auto value =
-            StringPrintf("%s/%s", to_string(type->type).data(), entry->name.c_str());
+              StringPrintf("%s/%s", type->named_type.to_string().data(), entry->name.c_str());
           items.push_back(DumpOverlayableEntry{overlayable_section, policy_subsection, value});
         }
       }
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 0bb330e..df8c3b9 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -139,10 +139,10 @@
 }
 
 std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s) {
-  auto colon = std::find(s.begin(), s.end(), ':');
+  auto dot = std::find(s.begin(), s.end(), '.');
   const ResourceType* parsedType;
-  if (colon != s.end() && colon != std::prev(s.end())) {
-    parsedType = ParseResourceType(s.substr(s.begin(), colon));
+  if (dot != s.end() && dot != std::prev(s.end())) {
+    parsedType = ParseResourceType(s.substr(s.begin(), dot));
   } else {
     parsedType = ParseResourceType(s);
   }
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 8d35eee..a99e4b2 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -981,12 +981,14 @@
     return false;
   }
 
-  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
-  if (!parsed_type) {
+  std::optional<ResourceNamedTypeRef> maybe_parsed_type =
+      ParseResourceNamedType(maybe_type.value());
+  if (!maybe_parsed_type) {
     diag->Error(DiagMessage(out_resource->source)
                 << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
     return false;
   }
+  auto parsed_type = maybe_parsed_type->ToResourceNamedType();
 
   std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
   if (!maybe_id_str) {
@@ -1046,7 +1048,7 @@
       }
 
       ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
-          .name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
+          .name = ResourceName{{}, parsed_type, maybe_name.value().to_string()},
           .source = item_source,
           .comment = std::move(comment),
       });
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 98cce26..0f5118d 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -43,8 +43,9 @@
 const char* Overlayable::kActorScheme = "overlay";
 
 namespace {
-bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
-  return lhs->type < rhs;
+bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs,
+                    const ResourceNamedTypeRef& rhs) {
+  return lhs->named_type < rhs;
 }
 
 template <typename T>
@@ -115,18 +116,24 @@
 }
 
 template <typename Func, typename Elements>
-static ResourceTableType* FindTypeRunAction(ResourceType type, Elements& entries, Func action) {
+static ResourceTableType* FindTypeRunAction(const ResourceNamedTypeRef& type, Elements& entries,
+                                            Func action) {
   const auto iter = std::lower_bound(entries.begin(), entries.end(), type, less_than_type);
-  const bool found = iter != entries.end() && type == (*iter)->type;
+  const bool found = iter != entries.end() && type == (*iter)->named_type;
   return action(found, iter);
 }
 
-ResourceTableType* ResourceTablePackage::FindType(ResourceType type) const {
+ResourceTableType* ResourceTablePackage::FindTypeWithDefaultName(const ResourceType type) const {
+  auto named_type = ResourceNamedTypeWithDefaultName(type);
+  return FindType(named_type);
+}
+
+ResourceTableType* ResourceTablePackage::FindType(const ResourceNamedTypeRef& type) const {
   return FindTypeRunAction(type, types,
                            [&](bool found, auto& iter) { return found ? iter->get() : nullptr; });
 }
 
-ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
+ResourceTableType* ResourceTablePackage::FindOrCreateType(const ResourceNamedTypeRef& type) {
   return FindTypeRunAction(type, types, [&](bool found, auto& iter) {
     return found ? iter->get() : types.emplace(iter, new ResourceTableType(type))->get();
   });
@@ -329,7 +336,7 @@
 
 struct TypeViewComparer {
   bool operator()(const ResourceTableTypeView& lhs, const ResourceTableTypeView& rhs) {
-    return lhs.id != rhs.id ? lhs.id < rhs.id : lhs.type < rhs.type;
+    return lhs.id != rhs.id ? lhs.id < rhs.id : lhs.named_type < rhs.named_type;
   }
 };
 
@@ -355,7 +362,8 @@
                                        id ? id.value().package_id() : std::optional<uint8_t>{}};
   auto view_package = package_inserter.Insert(table.packages, std::move(new_package));
 
-  ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : std::optional<uint8_t>{}};
+  ResourceTableTypeView new_type{type->named_type,
+                                 id ? id.value().type_id() : std::optional<uint8_t>{}};
   auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
 
   if (visibility.level == Visibility::Level::kPublic) {
@@ -420,13 +428,14 @@
     // we can reuse those packages for other types that need to be extracted from this package.
     // `start_index` is the index of the first newly created package that can be reused.
     const size_t start_index = new_packages.size();
-    std::map<ResourceType, size_t> type_new_package_index;
+    std::map<ResourceNamedType, size_t> type_new_package_index;
     for (auto type_it = package.types.begin(); type_it != package.types.end();) {
       auto& type = *type_it;
-      auto type_index_iter = type_new_package_index.find(type.type);
+      auto type_index_iter = type_new_package_index.find(type.named_type);
       if (type_index_iter == type_new_package_index.end()) {
         // First occurrence of the resource type in this package. Keep it in this package.
-        type_new_package_index.insert(type_index_iter, std::make_pair(type.type, start_index));
+        type_new_package_index.insert(type_index_iter,
+                                      std::make_pair(type.named_type, start_index));
         ++type_it;
         continue;
       }
@@ -440,7 +449,7 @@
 
       // Move the type into a new package
       auto& other_package = new_packages[index];
-      type_new_package_index[type.type] = index + 1;
+      type_new_package_index[type.named_type] = index + 1;
       type_inserter.Insert(other_package.types, std::move(type));
       type_it = package.types.erase(type_it);
     }
@@ -473,7 +482,7 @@
   }
 
   auto package = FindOrCreatePackage(res.name.package);
-  auto type = package->FindOrCreateType(res.name.type.type);
+  auto type = package->FindOrCreateType(res.name.type);
   auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), res.name.entry,
                                    NameEqualRange<ResourceEntry>{});
   const size_t entry_count = std::distance(entry_it.first, entry_it.second);
@@ -593,7 +602,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type.type);
+  ResourceTableType* type = package->FindType(name.type);
   if (type == nullptr) {
     return {};
   }
@@ -612,7 +621,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type.type);
+  ResourceTableType* type = package->FindType(name.type);
   if (type == nullptr) {
     return {};
   }
@@ -633,7 +642,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type.type);
+  ResourceTableType* type = package->FindType(name.type);
   if (type == nullptr) {
     return {};
   }
@@ -655,7 +664,7 @@
   for (const auto& pkg : packages) {
     ResourceTablePackage* new_pkg = new_table->FindOrCreatePackage(pkg->name);
     for (const auto& type : pkg->types) {
-      ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
+      ResourceTableType* new_type = new_pkg->FindOrCreateType(type->named_type);
       new_type->visibility_level = type->visibility_level;
 
       for (const auto& entry : type->entries) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 2e17659..7aa8b0f 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -168,7 +168,7 @@
 class ResourceTableType {
  public:
   // The logical type of resource (string, drawable, layout, etc.).
-  const ResourceType type;
+  const ResourceNamedType named_type;
 
   // Whether this type is public (and must maintain the same type ID across builds).
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
@@ -176,7 +176,9 @@
   // List of resources for this type.
   std::vector<std::unique_ptr<ResourceEntry>> entries;
 
-  explicit ResourceTableType(const ResourceType type) : type(type) {}
+  explicit ResourceTableType(const ResourceNamedTypeRef& type)
+      : named_type(type.ToResourceNamedType()) {
+  }
 
   ResourceEntry* CreateEntry(const android::StringPiece& name);
   ResourceEntry* FindEntry(const android::StringPiece& name) const;
@@ -196,8 +198,9 @@
   }
 
   ResourceTablePackage() = default;
-  ResourceTableType* FindType(ResourceType type) const;
-  ResourceTableType* FindOrCreateType(ResourceType type);
+  ResourceTableType* FindTypeWithDefaultName(const ResourceType type) const;
+  ResourceTableType* FindType(const ResourceNamedTypeRef& type) const;
+  ResourceTableType* FindOrCreateType(const ResourceNamedTypeRef& type);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
@@ -217,7 +220,7 @@
 };
 
 struct ResourceTableTypeView {
-  ResourceType type;
+  ResourceNamedType named_type;
   std::optional<uint8_t> id;
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
 
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 23f6c88..b4e79ca 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -40,6 +40,23 @@
 namespace aapt {
 namespace ResourceUtils {
 
+static std::optional<ResourceNamedType> ToResourceNamedType(const char16_t* type16,
+                                                            const char* type, size_t type_len) {
+  std::optional<ResourceNamedTypeRef> parsed_type;
+  if (type16) {
+    auto converted = util::Utf16ToUtf8(StringPiece16(type16, type_len));
+    parsed_type = ParseResourceNamedType(converted);
+  } else if (type) {
+    parsed_type = ParseResourceNamedType(StringPiece(type, type_len));
+  } else {
+    return {};
+  }
+  if (!parsed_type) {
+    return {};
+  }
+  return parsed_type->ToResourceNamedType();
+}
+
 std::optional<ResourceName> ToResourceName(const android::ResTable::resource_name& name_in) {
   // TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2
   ResourceName name_out;
@@ -50,20 +67,12 @@
   name_out.package =
       util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
 
-  std::optional<ResourceNamedTypeRef> type;
-  if (name_in.type) {
-    type = ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
-  } else if (name_in.type8) {
-    type = ParseResourceNamedType(StringPiece(name_in.type8, name_in.typeLen));
-  } else {
-    return {};
-  }
-
+  std::optional<ResourceNamedType> type =
+      ToResourceNamedType(name_in.type, name_in.name8, name_in.typeLen);
   if (!type) {
     return {};
   }
-
-  name_out.type = type->ToResourceNamedType();
+  name_out.type = *type;
 
   if (name_in.name) {
     name_out.entry =
@@ -84,21 +93,12 @@
 
   name_out.package = std::string(name_in.package, name_in.package_len);
 
-  std::optional<ResourceNamedTypeRef> type;
-  if (name_in.type16) {
-    type =
-        ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
-  } else if (name_in.type) {
-    type = ParseResourceNamedType(StringPiece(name_in.type, name_in.type_len));
-  } else {
-    return {};
-  }
-
+  std::optional<ResourceNamedType> type =
+      ToResourceNamedType(name_in.type16, name_in.type, name_in.type_len);
   if (!type) {
     return {};
   }
-
-  name_out.type = type->ToResourceNamedType();
+  name_out.type = *type;
 
   if (name_in.entry16) {
     name_out.entry =
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index 2c55d1d..01d3c84 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -135,13 +135,13 @@
   type = ParseResourceNamedType("layout");
   EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout", ResourceType::kLayout))));
 
-  type = ParseResourceNamedType("layout:2");
-  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:2", ResourceType::kLayout))));
+  type = ParseResourceNamedType("layout.2");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout.2", ResourceType::kLayout))));
 
-  type = ParseResourceNamedType("layout:another");
-  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:another", ResourceType::kLayout))));
+  type = ParseResourceNamedType("layout.another");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout.another", ResourceType::kLayout))));
 
-  type = ParseResourceNamedType("layout:");
+  type = ParseResourceNamedType("layout.");
   EXPECT_THAT(type, Eq(std::nullopt));
 
   type = ParseResourceNamedType("layout2");
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index fe56018..e27b9aa 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -243,9 +243,9 @@
                 r_txt_printer.Print("private ");
             }
 
-            if (type->type != ResourceType::kStyleable) {
+            if (type->named_type.type != ResourceType::kStyleable) {
               r_txt_printer.Print("int ");
-              r_txt_printer.Print(to_string(type->type));
+              r_txt_printer.Print(type->named_type.to_string());
               r_txt_printer.Print(" ");
               r_txt_printer.Println(entry->name);
             } else {
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index d9e8c92..a854146 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -105,7 +105,7 @@
   Value* value_b = config_value_b->value.get();
   if (!value_a->Equals(value_b)) {
     std::stringstream str_stream;
-    str_stream << "value " << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
+    str_stream << "value " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
                << " config=" << config_value_a->config << " does not match:\n";
     value_a->Print(&str_stream);
     str_stream << "\n vs \n";
@@ -128,7 +128,7 @@
     auto config_value_b = entry_b.FindValue(config_value_a->config);
     if (!config_value_b) {
       std::stringstream str_stream;
-      str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
+      str_stream << "missing " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
                  << " config=" << config_value_a->config;
       EmitDiffLine(apk_b->GetSource(), str_stream.str());
       diff = true;
@@ -143,7 +143,7 @@
     auto config_value_a = entry_a.FindValue(config_value_b->config);
     if (!config_value_a) {
       std::stringstream str_stream;
-      str_stream << "new config " << pkg_b.name << ":" << type_b.type << "/" << entry_b.name
+      str_stream << "new config " << pkg_b.name << ":" << type_b.named_type << "/" << entry_b.name
                  << " config=" << config_value_b->config;
       EmitDiffLine(apk_b->GetSource(), str_stream.str());
       diff = true;
@@ -164,13 +164,15 @@
     if (entry_b_iter == type_b.entries.end()) {
       // Type A contains a type that type B does not have.
       std::stringstream str_stream;
-      str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a_iter->name;
+      str_stream << "missing " << pkg_a.name << ":" << type_a.named_type << "/"
+                 << entry_a_iter->name;
       EmitDiffLine(apk_a->GetSource(), str_stream.str());
       diff = true;
     } else if (entry_a_iter == type_a.entries.end()) {
       // Type B contains a type that type A does not have.
       std::stringstream str_stream;
-      str_stream << "new entry " << pkg_b.name << ":" << type_b.type << "/" << entry_b_iter->name;
+      str_stream << "new entry " << pkg_b.name << ":" << type_b.named_type << "/"
+                 << entry_b_iter->name;
       EmitDiffLine(apk_b->GetSource(), str_stream.str());
       diff = true;
     } else {
@@ -178,7 +180,7 @@
       const auto& entry_b = *entry_b_iter;
       if (IsSymbolVisibilityDifferent(entry_a.visibility, entry_b.visibility)) {
         std::stringstream str_stream;
-        str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
+        str_stream << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
                    << " has different visibility (";
         if (entry_b.visibility.staged_api) {
           str_stream << "STAGED ";
@@ -203,7 +205,7 @@
       } else if (IsIdDiff(entry_a.visibility.level, entry_a.id, entry_b.visibility.level,
                           entry_b.id)) {
         std::stringstream str_stream;
-        str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
+        str_stream << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
                    << " has different public ID (";
         if (entry_b.id) {
           str_stream << "0x" << std::hex << entry_b.id.value();
@@ -243,13 +245,13 @@
     if (type_b_iter == pkg_b.types.end()) {
       // Type A contains a type that type B does not have.
       std::stringstream str_stream;
-      str_stream << "missing " << pkg_a.name << ":" << type_a_iter->type;
+      str_stream << "missing " << pkg_a.name << ":" << type_a_iter->named_type;
       EmitDiffLine(apk_a->GetSource(), str_stream.str());
       diff = true;
     } else if (type_a_iter == pkg_a.types.end()) {
       // Type B contains a type that type A does not have.
       std::stringstream str_stream;
-      str_stream << "new type " << pkg_b.name << ":" << type_b_iter->type;
+      str_stream << "new type " << pkg_b.name << ":" << type_b_iter->named_type;
       EmitDiffLine(apk_b->GetSource(), str_stream.str());
       diff = true;
     } else {
@@ -257,7 +259,7 @@
       const auto& type_b = *type_b_iter;
       if (type_a.visibility_level != type_b.visibility_level) {
         std::stringstream str_stream;
-        str_stream << pkg_a.name << ":" << type_a.type << " has different visibility (";
+        str_stream << pkg_a.name << ":" << type_a.named_type << " has different visibility (";
         if (type_b.visibility_level == Visibility::Level::kPublic) {
           str_stream << "PUBLIC";
         } else {
@@ -274,7 +276,7 @@
         diff = true;
       } else if (IsIdDiff(type_a.visibility_level, type_a.id, type_b.visibility_level, type_b.id)) {
         std::stringstream str_stream;
-        str_stream << pkg_a.name << ":" << type_a.type << " has different public ID (";
+        str_stream << pkg_a.name << ":" << type_a.named_type << " has different public ID (";
         if (type_b.id) {
           str_stream << "0x" << std::hex << type_b.id.value();
         } else {
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 790f2b3..bd74cc7 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -556,7 +556,7 @@
           file_op.config = config_value->config;
           file_op.file_to_copy = file;
 
-          if (type->type != ResourceType::kRaw &&
+          if (type->named_type.type != ResourceType::kRaw &&
               (file_ref->type == ResourceFile::Type::kBinaryXml ||
                file_ref->type == ResourceFile::Type::kProtoXml)) {
             std::unique_ptr<io::IData> data = file->OpenAsData();
@@ -596,7 +596,8 @@
 
             file_op.xml_to_flatten->file.config = config_value->config;
             file_op.xml_to_flatten->file.source = file_ref->GetSource();
-            file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name);
+            file_op.xml_to_flatten->file.name =
+                ResourceName(pkg->name, type->named_type, entry->name);
           }
 
           // NOTE(adamlesinski): Explicitly construct a StringPiece here, or
@@ -1009,7 +1010,7 @@
         // We have a package that is not related to the one we're building!
         for (const auto& type : package->types) {
           for (const auto& entry : type->entries) {
-            ResourceNameRef res_name(package->name, type->type, entry->name);
+            ResourceNameRef res_name(package->name, type->named_type, entry->name);
 
             for (const auto& config_value : entry->values) {
               // Special case the occurrence of an ID that is being generated
@@ -1046,7 +1047,7 @@
       for (const auto& type : package->types) {
         for (const auto& entry : type->entries) {
           if (entry->id) {
-            ResourceNameRef res_name(package->name, type->type, entry->name);
+            ResourceNameRef res_name(package->name, type->named_type, entry->name);
             context_->GetDiagnostics()->Error(DiagMessage() << "resource " << res_name << " has ID "
                                                             << entry->id.value() << " assigned");
             return false;
@@ -1057,6 +1058,83 @@
     return true;
   }
 
+  bool VerifyLocaleFormat(xml::XmlResource* manifest, IDiagnostics* diag) {
+    // Skip it if the Manifest doesn't declare the localeConfig attribute within the <application>
+    // element.
+    const xml::Element* application = manifest->root->FindChild("", "application");
+    if (!application) {
+      return true;
+    }
+    const xml::Attribute* localeConfig =
+        application->FindAttribute(xml::kSchemaAndroid, "localeConfig");
+    if (!localeConfig) {
+      return true;
+    }
+
+    if (localeConfig->compiled_value) {
+      const auto localeconfig_reference = ValueCast<Reference>(localeConfig->compiled_value.get());
+      const auto localeconfig_entry =
+          ResolveTableEntry(context_, &final_table_, localeconfig_reference);
+      if (!localeconfig_entry) {
+        return true;
+      }
+
+      for (const auto& value : localeconfig_entry->values) {
+        // Load an XML file which is linked from the localeConfig attribute.
+        const std::string& path = value->value->GetSource().path;
+        std::unique_ptr<xml::XmlResource> localeConfig_xml = LoadXml(path, diag);
+        if (!localeConfig_xml) {
+          diag->Error(DiagMessage(path) << "can't load the XML");
+          return false;
+        }
+
+        xml::Element* localeConfig_el = xml::FindRootElement(localeConfig_xml->root.get());
+        if (!localeConfig_el) {
+          diag->Error(DiagMessage(path) << "no root tag defined");
+          return false;
+        }
+        if (localeConfig_el->name != "locale-config") {
+          diag->Error(DiagMessage(path) << "invalid element name: " << localeConfig_el->name
+                                        << ", expected: locale-config");
+          return false;
+        }
+
+        for (const xml::Element* child_el : localeConfig_el->GetChildElements()) {
+          if (child_el->name == "locale") {
+            if (const xml::Attribute* locale_name_attr =
+                    child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
+              const std::string& locale_name = locale_name_attr->value;
+              const std::string valid_name = ConvertToBCP47Tag(locale_name);
+
+              // Start to verify the locale format
+              ConfigDescription config;
+              if (!ConfigDescription::Parse(valid_name, &config)) {
+                diag->Error(DiagMessage(path) << "invalid configuration: " << locale_name);
+                return false;
+              }
+            } else {
+              diag->Error(DiagMessage(path) << "the attribute android:name is not found");
+              return false;
+            }
+          } else {
+            diag->Error(DiagMessage(path)
+                        << "invalid element name: " << child_el->name << ", expected: locale");
+            return false;
+          }
+        }
+      }
+    }
+    return true;
+  }
+
+  std::string ConvertToBCP47Tag(const std::string& locale) {
+    std::string bcp47tag = "b+";
+    bcp47tag += locale;
+    std::replace(bcp47tag.begin(), bcp47tag.end(), '-', '+');
+
+    return bcp47tag;
+  }
+
   std::unique_ptr<IArchiveWriter> MakeArchiveWriter(const StringPiece& out) {
     if (options_.output_to_directory) {
       return CreateDirectoryArchiveWriter(context_->GetDiagnostics(), out);
@@ -1939,7 +2017,7 @@
         for (auto& package : final_table_.packages) {
           for (auto& type : package->types) {
             for (auto& entry : type->entries) {
-              ResourceName name(package->name, type->type, entry->name);
+              ResourceName name(package->name, type->named_type, entry->name);
               // The IDs are guaranteed to exist.
               options_.stable_id_map[std::move(name)] = entry->id.value();
             }
@@ -2180,6 +2258,10 @@
       return 1;
     }
 
+    if (!VerifyLocaleFormat(manifest_xml.get(), context_->GetDiagnostics())) {
+      return 1;
+    };
+
     if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
       return 1;
     }
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 430c184..7b1236a 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -22,6 +22,7 @@
 #include "LoadedApk.h"
 #include "test/Test.h"
 
+using android::ConfigDescription;
 using testing::Eq;
 using testing::HasSubstr;
 using testing::IsNull;
@@ -783,4 +784,51 @@
   EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
 }
 
+TEST_F(LinkTest, ParseLocaleConfig) {
+  StdErrDiagnostics diag;
+  const std::string xml_values =
+      R"(<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
+            <locale android:name="pt"/>
+            <locale android:name="chr"/>
+            <locale android:name="chr-US"/>
+            <locale android:name="zh-Hant"/>
+            <locale android:name="es-419"/>
+            <locale android:name="en-US"/>
+            <locale android:name="zh-Hans-SG"/>
+        </locale-config>)";
+
+  const std::string res = GetTestPath("test-res");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locale_config.xml"), xml_values, res, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  auto link_args = LinkCommandBuilder(this)
+                       .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
+                       .AddCompiledResDir(res, &diag)
+                       .AddFlag("--no-auto-version")
+                       .Build(out_apk);
+  ASSERT_TRUE(Link(link_args, &diag));
+
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+  ASSERT_THAT(apk, Ne(nullptr));
+
+  auto xml = apk->LoadXml("res/xml/locale_config.xml", &diag);
+  ASSERT_THAT(xml, NotNull());
+  EXPECT_THAT(xml->root->name, Eq("locale-config"));
+  ASSERT_THAT(xml->root->children.size(), Eq(7));
+  for (auto& node : xml->root->children) {
+    const xml::Element* child_el = xml::NodeCast<xml::Element>(node.get());
+    ASSERT_THAT(child_el, NotNull());
+    EXPECT_THAT(child_el->name, Eq("locale"));
+
+    auto& xml_attrs = child_el->attributes;
+    for (auto& attr : xml_attrs) {
+      std::string locale = "b+";
+      locale += attr.value;
+      std::replace(locale.begin(), locale.end(), '-', '+');
+      ConfigDescription config;
+      ASSERT_TRUE(ConfigDescription::Parse(locale, &config));
+    }
+  }
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index caa3e60..e1370fd 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -254,7 +254,7 @@
             }
 
             if (file_ref->file == nullptr) {
-              ResourceNameRef name(pkg->name, type->type, entry->name);
+              ResourceNameRef name(pkg->name, type->named_type, entry->name);
               context_->GetDiagnostics()->Warn(DiagMessage(file_ref->GetSource())
                                                << "file for resource " << name << " with config '"
                                                << config_value->config << "' not found");
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index fa816be..29f9a08 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -128,7 +128,7 @@
   for (auto& package : table->packages) {
     for (auto& type : package->types) {
       for (auto& entry : type->entries) {
-        const ResourceName name(package->name, type->type, entry->name);
+        const ResourceName name(package->name, type->named_type, entry->name);
         if (entry->id && !assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
                                                  context->GetDiagnostics())) {
           return false;
@@ -175,7 +175,7 @@
   for (auto& package : table->packages) {
     for (auto& type : package->types) {
       for (auto& entry : type->entries) {
-        const ResourceName name(package->name, type->type, entry->name);
+        const ResourceName name(package->name, type->named_type, entry->name);
         if (entry->id) {
           continue;
         }
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index d357571..8911dad 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -191,12 +191,12 @@
       for (auto& entry : type->entries) {
         if (!entry->id) {
           return ::testing::AssertionFailure()
-                 << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+                 << "resource " << ResourceNameRef(package->name, type->named_type, entry->name)
                  << " has no ID";
         }
         if (!seen_ids.insert(entry->id.value()).second) {
           return ::testing::AssertionFailure()
-                 << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+                 << "resource " << ResourceNameRef(package->name, type->named_type, entry->name)
                  << " has a non-unique ID" << std::hex << entry->id.value() << std::dec;
         }
       }
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index c65c550..eea7efc 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -18,19 +18,19 @@
 
 #include <algorithm>
 #include <map>
+#include <optional>
 #include <string>
 
-#include "android-base/logging.h"
-#include "android-base/macros.h"
-#include "android-base/stringprintf.h"
-#include "androidfw/ResourceTypes.h"
-#include "androidfw/TypeWrappers.h"
-
 #include "ResourceTable.h"
 #include "ResourceUtils.h"
 #include "ResourceValues.h"
 #include "Source.h"
 #include "ValueVisitor.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/TypeWrappers.h"
 #include "format/binary/ResChunkPullParser.h"
 #include "util/Util.h"
 
@@ -364,7 +364,7 @@
   config.copyFromDtoH(type->config);
 
   const std::string type_str = util::GetString(type_pool_, type->id - 1);
-  const ResourceType* parsed_type = ParseResourceType(type_str);
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(type_str);
   if (!parsed_type) {
     diag_->Warn(DiagMessage(source_)
                 << "invalid type name '" << type_str << "' for type with ID " << type->id);
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index a9192e8..b42e7d0 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -360,7 +360,7 @@
       if (!FlattenValue(&flat_entry, &values_buffer)) {
         diag_->Error(DiagMessage()
                      << "failed to flatten resource '"
-                     << ResourceNameRef(package_.name, type.type, flat_entry.entry->name)
+                     << ResourceNameRef(package_.name, type.named_type, flat_entry.entry->name)
                      << "' for configuration '" << config << "'");
         return false;
       }
@@ -447,7 +447,7 @@
         ResourceId id = android::make_resid(package_.id.value(), type.id.value(), entry.id.value());
         CHECK(seen_ids.find(id) == seen_ids.end())
             << "multiple overlayable definitions found for resource "
-            << ResourceName(package_.name, type.type, entry.name).to_string();
+            << ResourceName(package_.name, type.named_type, entry.name).to_string();
         seen_ids.insert(id);
 
         // Find the overlayable chunk with the specified name
@@ -592,7 +592,8 @@
   bool FlattenTypes(BigBuffer* buffer) {
     size_t expected_type_id = 1;
     for (const ResourceTableTypeView& type : package_.types) {
-      if (type.type == ResourceType::kStyleable || type.type == ResourceType::kMacro) {
+      if (type.named_type.type == ResourceType::kStyleable ||
+          type.named_type.type == ResourceType::kMacro) {
         // Styleables and macros are not real resource types.
         continue;
       }
@@ -606,7 +607,7 @@
         expected_type_id++;
       }
       expected_type_id++;
-      type_pool_.MakeRef(to_string(type.type));
+      type_pool_.MakeRef(type.named_type.to_string());
 
       if (!FlattenTypeSpec(type, type.entries, buffer)) {
         return false;
@@ -634,7 +635,7 @@
         }
 
         uint32_t local_key_index;
-        ResourceName resource_name({}, type.type, entry.name);
+        ResourceName resource_name({}, type.named_type, entry.name);
         if (!collapse_key_stringpool_ ||
             name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) {
           local_key_index = (uint32_t)key_pool_.MakeRef(entry.name).index();
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index cd1c0af..c73bbb5 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -837,4 +837,45 @@
   ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table));
 }
 
+TEST_F(TableFlattenerTest, FlattenCustomResourceTypes) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddSimple("com.app.test:id/one", ResourceId(0x7f010000))
+          .AddSimple("com.app.test:id.2/two", ResourceId(0x7f020000))
+          .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 10u))
+          .AddValue("com.app.test:integer.1/one", ResourceId(0x7f040000),
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
+          .AddValue("com.app.test:integer.1/one", test::ParseConfigOrDie("v1"),
+                    ResourceId(0x7f040000),
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
+          .AddString("com.app.test:layout.custom/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
+          .Build();
+
+  ResTable res_table;
+  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f010000), {},
+                     Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id.2/two", ResourceId(0x7f020000), {},
+                     Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {},
+                     Res_value::TYPE_INT_DEC, 10u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer.1/one", ResourceId(0x7f040000), {},
+                     Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer.1/one", ResourceId(0x7f040000),
+                     test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
+                     ResTable_config::CONFIG_VERSION));
+
+  std::u16string bar_path = u"res/layout/bar.xml";
+  auto idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
+  ASSERT_TRUE(idx.has_value());
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:layout.custom/bar", ResourceId(0x7f050000), {},
+                     Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 236c381..82c7248 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -429,8 +429,8 @@
 
   ResourceTablePackage* pkg = out_table->FindOrCreatePackage(pb_package.package_name());
   for (const pb::Type& pb_type : pb_package.type()) {
-    const ResourceType* res_type = ParseResourceType(pb_type.name());
-    if (res_type == nullptr) {
+    auto res_type = ParseResourceNamedType(pb_type.name());
+    if (!res_type) {
       std::ostringstream error;
       error << "unknown type '" << pb_type.name() << "'";
       *out_error = error.str();
@@ -515,7 +515,7 @@
       ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
                        pb_entry.entry_id().id());
       if (resid.is_valid()) {
-        id_index[resid] = ResourceNameRef(pkg->name, type->type, entry->name);
+        id_index[resid] = ResourceNameRef(pkg->name, type->named_type, entry->name);
       }
 
       for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index f3b7f75..bb8ea0c 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -358,7 +358,7 @@
       if (type.id) {
         pb_type->mutable_type_id()->set_id(type.id.value());
       }
-      pb_type->set_name(to_string(type.type).to_string());
+      pb_type->set_name(type.named_type.to_string());
 
       // hardcoded string uses characters which make it an invalid resource name
       static const char* obfuscated_resource_name = "0_resource_name_obfuscated";
@@ -367,7 +367,7 @@
         if (entry.id) {
           pb_entry->mutable_entry_id()->set_id(entry.id.value());
         }
-        ResourceName resource_name({}, type.type, entry.name);
+        ResourceName resource_name({}, type.named_type, entry.name);
         if (options.collapse_key_stringpool &&
             options.name_collapse_exemptions.find(resource_name) ==
             options.name_collapse_exemptions.end()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index d1d72e0..0247021 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -951,4 +951,76 @@
   EXPECT_THAT(result.value().entry->staged_id.value().id, Eq(ResourceId(0x01ff0001)));
 }
 
+TEST(ProtoSerializeTest, CustomResourceTypes) {
+  const uint32_t id_one_id = 0x7f020000;
+  const uint32_t id_2_two_id = 0x7f030000;
+  const uint32_t integer_three_id = 0x7f030000;
+  const uint32_t integer_1_four_id = 0x7f030000;
+  const uint32_t layout_bar_id = 0x7f050000;
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddSimple("com.app.test:id/one", ResourceId(id_one_id))
+          .AddSimple("com.app.test:id.2/two", ResourceId(id_2_two_id))
+          .AddValue(
+              "com.app.test:integer/one", ResourceId(integer_three_id),
+              util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 10u))
+          .AddValue(
+              "com.app.test:integer.1/one", ResourceId(integer_1_four_id),
+              util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 1u))
+          .AddValue(
+              "com.app.test:integer.1/one", test::ParseConfigOrDie("v1"),
+              ResourceId(integer_1_four_id),
+              util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 2u))
+          .AddFileReference("com.app.test:layout.custom/bar", ResourceId(layout_bar_id),
+                            "res/layout/bar.xml")
+          .Build();
+
+  test::TestFile file_a("res/layout/bar.xml");
+  MockFileCollection files;
+  EXPECT_CALL(files, FindFile(Eq("res/layout/bar.xml"))).WillRepeatedly(::testing::Return(&file_a));
+
+  ResourceTable new_table;
+  pb::ResourceTable pb_table;
+  std::string error;
+  SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+  DeserializeTableFromPb(pb_table, &files, &new_table, &error);
+  ASSERT_THAT(error, IsEmpty());
+
+  auto bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(
+      &new_table, "com.app.test:integer.1/one", ConfigDescription::DefaultConfig(), "");
+  ASSERT_THAT(bp, NotNull());
+  EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC));
+  EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("1")->value.data));
+
+  bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer.1/one",
+                                                          test::ParseConfigOrDie("v1"), "");
+  ASSERT_THAT(bp, NotNull());
+  EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC));
+  EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("2")->value.data));
+
+  bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer/one",
+                                                          ConfigDescription::DefaultConfig(), "");
+  ASSERT_THAT(bp, NotNull());
+  EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC));
+  EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("10")->value.data));
+
+  bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer/one",
+                                                          test::ParseConfigOrDie("v1"), "");
+  ASSERT_THAT(bp, IsNull());
+
+  auto id = test::GetValueForConfigAndProduct<Id>(&new_table, "com.app.test:id/one",
+                                                  ConfigDescription::DefaultConfig(), "");
+  ASSERT_THAT(id, NotNull());
+
+  id = test::GetValueForConfigAndProduct<Id>(&new_table, "com.app.test:id.2/two",
+                                             ConfigDescription::DefaultConfig(), "");
+  ASSERT_THAT(id, NotNull());
+
+  auto custom_layout = test::GetValueForConfigAndProduct<FileReference>(
+      &new_table, "com.app.test:layout.custom/bar", ConfigDescription::DefaultConfig(), "");
+  ASSERT_THAT(custom_layout, NotNull());
+  EXPECT_THAT(*(custom_layout->path), Eq("res/layout/bar.xml"));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index a963d98..a25ca22 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -548,10 +548,11 @@
     }
 
     // We need to make sure we hide the fact that we are generating kAttrPrivate attributes.
-    const ResourceNameRef resource_name(
-        package_name_to_generate,
-        type.type == ResourceType::kAttrPrivate ? ResourceType::kAttr : type.type,
-        unmangled_name.value());
+    const auto target_type = type.named_type.type == ResourceType::kAttrPrivate
+                                 ? ResourceNamedTypeWithDefaultName(ResourceType::kAttr)
+                                 : type.named_type;
+    const ResourceNameRef resource_name(package_name_to_generate, target_type,
+                                        unmangled_name.value());
 
     // Check to see if the unmangled name is a valid Java name (not a keyword).
     if (!IsValidSymbol(unmangled_name.value())) {
@@ -616,7 +617,8 @@
 
   for (const auto& package : table_->packages) {
     for (const auto& type : package->types) {
-      if (type->type == ResourceType::kAttrPrivate || type->type == ResourceType::kMacro) {
+      if (type->named_type.type == ResourceType::kAttrPrivate ||
+          type->named_type.type == ResourceType::kMacro) {
         // We generate kAttrPrivate as part of the kAttr type, so skip them here.
         // Macros are not actual resources, so skip them as well.
         continue;
@@ -628,7 +630,7 @@
       std::unique_ptr<ClassDefinition> class_def;
       if (out != nullptr) {
         class_def = util::make_unique<ClassDefinition>(
-            to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
+            to_string(type->named_type.type), ClassQualifier::kStatic, force_creation_if_empty);
       }
 
       if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
@@ -636,9 +638,10 @@
         return false;
       }
 
-      if (type->type == ResourceType::kAttr) {
+      if (type->named_type.type == ResourceType::kAttr) {
         // Also include private attributes in this same class.
-        if (const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate)) {
+        if (const ResourceTableType* priv_type =
+                package->FindTypeWithDefaultName(ResourceType::kAttrPrivate)) {
           if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
                            rewrite_method.get(), r_txt_printer.get())) {
             return false;
@@ -646,7 +649,7 @@
         }
       }
 
-      if (out != nullptr && type->type == ResourceType::kStyleable && is_public) {
+      if (out != nullptr && type->named_type.type == ResourceType::kStyleable && is_public) {
         // When generating a public R class, we don't want Styleable to be part
         // of the API. It is only emitted for documentation purposes.
         class_def->GetCommentBuilder()->AppendComment("@doconly");
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index e53e220..80a46d5 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -517,7 +517,7 @@
     for (auto& type : pkg->types) {
       for (auto& entry : type->entries) {
         for (auto& config_value : entry->values) {
-          ResourceName from(pkg->name, type->type, entry->name);
+          ResourceName from(pkg->name, type->named_type, entry->name);
           ReferenceVisitor visitor(context, from, keep_set);
           config_value->value->Accept(&visitor);
         }
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 328ac97..3dbd7e6 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -75,7 +75,7 @@
   CloningValueTransformer cloner(&table->string_pool);
   for (auto& package : table->packages) {
     for (auto& type : package->types) {
-      if (type->type != ResourceType::kStyle) {
+      if (type->named_type.type != ResourceType::kStyle) {
         continue;
       }
 
diff --git a/tools/aapt2/link/NoDefaultResourceRemover.cpp b/tools/aapt2/link/NoDefaultResourceRemover.cpp
index 05990de..ab3c04e 100644
--- a/tools/aapt2/link/NoDefaultResourceRemover.cpp
+++ b/tools/aapt2/link/NoDefaultResourceRemover.cpp
@@ -76,7 +76,7 @@
       });
 
       for (auto iter = remove_iter; iter != end_iter; ++iter) {
-        const ResourceName name(pkg->name, type->type, (*iter)->name);
+        const ResourceName name(pkg->name, type->named_type, (*iter)->name);
         IDiagnostics* diag = context->GetDiagnostics();
         diag->Warn(DiagMessage() << "removing resource " << name
                                  << " without required default value");
diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp
index 675b02a..8c6c743 100644
--- a/tools/aapt2/link/PrivateAttributeMover.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover.cpp
@@ -57,7 +57,7 @@
 
 bool PrivateAttributeMover::Consume(IAaptContext* context, ResourceTable* table) {
   for (auto& package : table->packages) {
-    ResourceTableType* type = package->FindType(ResourceType::kAttr);
+    ResourceTableType* type = package->FindTypeWithDefaultName(ResourceType::kAttr);
     if (!type) {
       continue;
     }
@@ -80,7 +80,8 @@
       continue;
     }
 
-    ResourceTableType* priv_attr_type = package->FindOrCreateType(ResourceType::kAttrPrivate);
+    auto attr_private_type = ResourceNamedTypeWithDefaultName(ResourceType::kAttrPrivate);
+    ResourceTableType* priv_attr_type = package->FindOrCreateType(attr_private_type);
     CHECK(priv_attr_type->entries.empty());
     priv_attr_type->entries = std::move(private_attr_entries);
   }
diff --git a/tools/aapt2/link/PrivateAttributeMover_test.cpp b/tools/aapt2/link/PrivateAttributeMover_test.cpp
index 168234b..32335b7 100644
--- a/tools/aapt2/link/PrivateAttributeMover_test.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover_test.cpp
@@ -41,13 +41,13 @@
   ResourceTablePackage* package = table->FindPackage("android");
   ASSERT_NE(package, nullptr);
 
-  ResourceTableType* type = package->FindType(ResourceType::kAttr);
+  ResourceTableType* type = package->FindTypeWithDefaultName(ResourceType::kAttr);
   ASSERT_NE(type, nullptr);
   ASSERT_EQ(type->entries.size(), 2u);
   EXPECT_NE(type->FindEntry("publicA"), nullptr);
   EXPECT_NE(type->FindEntry("publicB"), nullptr);
 
-  type = package->FindType(ResourceType::kAttrPrivate);
+  type = package->FindTypeWithDefaultName(ResourceType::kAttrPrivate);
   ASSERT_NE(type, nullptr);
   ASSERT_EQ(type->entries.size(), 2u);
   EXPECT_NE(type->FindEntry("privateA"), nullptr);
@@ -68,11 +68,11 @@
   ResourceTablePackage* package = table->FindPackage("android");
   ASSERT_NE(package, nullptr);
 
-  ResourceTableType* type = package->FindType(ResourceType::kAttr);
+  ResourceTableType* type = package->FindTypeWithDefaultName(ResourceType::kAttr);
   ASSERT_NE(type, nullptr);
   ASSERT_EQ(type->entries.size(), 2u);
 
-  type = package->FindType(ResourceType::kAttrPrivate);
+  type = package->FindTypeWithDefaultName(ResourceType::kAttrPrivate);
   ASSERT_EQ(type, nullptr);
 }
 
@@ -87,12 +87,12 @@
   ResourceTablePackage* package = table->FindPackage("android");
   ASSERT_NE(nullptr, package);
 
-  ASSERT_EQ(nullptr, package->FindType(ResourceType::kAttrPrivate));
+  ASSERT_EQ(nullptr, package->FindTypeWithDefaultName(ResourceType::kAttrPrivate));
 
   PrivateAttributeMover mover;
   ASSERT_TRUE(mover.Consume(context.get(), table.get()));
 
-  ASSERT_EQ(nullptr, package->FindType(ResourceType::kAttrPrivate));
+  ASSERT_EQ(nullptr, package->FindTypeWithDefaultName(ResourceType::kAttrPrivate));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/link/ProductFilter.cpp
index 793740a..0c54a73 100644
--- a/tools/aapt2/link/ProductFilter.cpp
+++ b/tools/aapt2/link/ProductFilter.cpp
@@ -98,7 +98,7 @@
             // End of the array, or we saw a different config,
             // so this must be the end of a range of products.
             // Select the product to keep from the set of products defined.
-            ResourceNameRef name(pkg->name, type->type, entry->name);
+            ResourceNameRef name(pkg->name, type->named_type, entry->name);
             auto value_to_keep = SelectProductToKeep(
                 name, start_range_iter, iter, context->GetDiagnostics());
             if (value_to_keep == iter) {
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 5372cf2..d1fbffa 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -468,7 +468,7 @@
     for (auto& type : package->types) {
       for (auto& entry : type->entries) {
         // First, unmangle the name if necessary.
-        ResourceName name(package->name, type->type, entry->name);
+        ResourceName name(package->name, type->named_type, entry->name);
         NameMangler::Unmangle(&name.entry, &name.package);
 
         // Symbol state information may be lost if there is no value for the resource.
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index d78f0ac..caaaba6 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -235,7 +235,7 @@
   bool error = false;
 
   for (auto& src_type : src_package->types) {
-    ResourceTableType* dst_type = main_package_->FindOrCreateType(src_type->type);
+    ResourceTableType* dst_type = main_package_->FindOrCreateType(src_type->named_type);
     if (!MergeType(context_, src, dst_type, src_type.get())) {
       error = true;
       continue;
@@ -254,7 +254,7 @@
         dst_entry = dst_type->FindEntry(entry_name);
       }
 
-      const ResourceNameRef res_name(src_package->name, src_type->type, src_entry->name);
+      const ResourceNameRef res_name(src_package->name, src_type->named_type, src_entry->name);
 
       if (!dst_entry) {
         context_->GetDiagnostics()->Error(DiagMessage(src)
@@ -349,7 +349,7 @@
   file_ref->file = file;
 
   ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package);
-  pkg->FindOrCreateType(file_desc.name.type.type)
+  pkg->FindOrCreateType(file_desc.name.type)
       ->FindOrCreateEntry(file_desc.name.entry)
       ->FindOrCreateValue(file_desc.config, {})
       ->value = std::move(file_ref);
diff --git a/tools/aapt2/optimize/ResourceFilter.cpp b/tools/aapt2/optimize/ResourceFilter.cpp
index 08c045b..db84b66 100644
--- a/tools/aapt2/optimize/ResourceFilter.cpp
+++ b/tools/aapt2/optimize/ResourceFilter.cpp
@@ -28,7 +28,7 @@
   for (auto& package : table->packages) {
     for (auto& type : package->types) {
       for (auto it = type->entries.begin(); it != type->entries.end(); ) {
-        ResourceName resource = ResourceName({}, type->type, (*it)->name);
+        ResourceName resource = ResourceName({}, type->named_type, (*it)->name);
         if (exclude_list_.find(resource) != exclude_list_.end()) {
           it = type->entries.erase(it);
         } else {
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 116b2ab9..85d150f 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -189,7 +189,7 @@
     }
 
     for (auto& type : pkg->types) {
-      if (type->type == ResourceType::kMipmap) {
+      if (type->named_type.type == ResourceType::kMipmap) {
         // Always keep mipmaps.
         continue;
       }
@@ -241,7 +241,7 @@
             // Create the same resource structure in the split. We do this lazily because we might
             // not have actual values for each type/entry.
             ResourceTablePackage* split_pkg = split_table->FindPackage(pkg->name);
-            ResourceTableType* split_type = split_pkg->FindOrCreateType(type->type);
+            ResourceTableType* split_type = split_pkg->FindOrCreateType(type->named_type);
             split_type->visibility_level = type->visibility_level;
 
             ResourceEntry* split_entry = split_type->FindOrCreateEntry(entry->name);
diff --git a/tools/lint/README.md b/tools/lint/README.md
index b534b62..c674d36 100644
--- a/tools/lint/README.md
+++ b/tools/lint/README.md
@@ -78,6 +78,7 @@
 ## Documentation
 
 - [Android Lint Docs](https://googlesamples.github.io/android-custom-lint-rules/)
+- [Lint Check Unit Testing](https://googlesamples.github.io/android-custom-lint-rules/api-guide/unit-testing.md.html)
 - [Android Lint source files](https://source.corp.google.com/studio-main/tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/)
 - [PSI source files](https://github.com/JetBrains/intellij-community/tree/master/java/java-psi-api/src/com/intellij/psi)
 - [UAST source files](https://upsource.jetbrains.com/idea-ce/structure/idea-ce-7b9b8cc138bbd90aec26433f82cd2c6838694003/uast/uast-common/src/org/jetbrains/uast)
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index a6fd9bb..859961a 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -19,6 +19,7 @@
 import com.android.tools.lint.client.api.IssueRegistry
 import com.android.tools.lint.client.api.Vendor
 import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.android.lint.parcel.SaferParcelChecker
 import com.google.auto.service.AutoService
 
 @AutoService(IssueRegistry::class)
@@ -33,7 +34,8 @@
             CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
             CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED,
             EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
-            EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
+            EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
+            SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
     )
 
     override val api: Int
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
index 8011b36..9f21618 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
@@ -16,6 +16,7 @@
 
 package com.google.android.lint
 
+import com.android.tools.lint.client.api.UElementHandler
 import com.android.tools.lint.detector.api.AnnotationInfo
 import com.android.tools.lint.detector.api.AnnotationOrigin
 import com.android.tools.lint.detector.api.AnnotationUsageInfo
@@ -32,22 +33,39 @@
 import com.intellij.psi.PsiAnnotation
 import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UAnnotation
 import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.UMethod
 
 /**
  * Lint Detector that ensures that any method overriding a method annotated
  * with @EnforcePermission is also annotated with the exact same annotation.
  * The intent is to surface the effective permission checks to the service
  * implementations.
+ *
+ * This is done with 2 mechanisms:
+ *  1. Visit any annotation usage, to ensure that any derived class will have
+ *     the correct annotation on each methods. This is for the top to bottom
+ *     propagation.
+ *  2. Visit any annotation, to ensure that if a method is annotated, it has
+ *     its ancestor also annotated. This is to avoid having an annotation on a
+ *     Java method without the corresponding annotation on the AIDL interface.
  */
 class EnforcePermissionDetector : Detector(), SourceCodeScanner {
 
     val ENFORCE_PERMISSION = "android.annotation.EnforcePermission"
+    val BINDER_CLASS = "android.os.Binder"
+    val JAVA_OBJECT = "java.lang.Object"
 
     override fun applicableAnnotations(): List<String> {
         return listOf(ENFORCE_PERMISSION)
     }
 
+    override fun getApplicableUastTypes(): List<Class<out UElement>> {
+        return listOf(UAnnotation::class.java)
+    }
+
     private fun areAnnotationsEquivalent(
         context: JavaContext,
         anno1: PsiAnnotation,
@@ -74,6 +92,73 @@
         return true
     }
 
+    private fun compareMethods(
+        context: JavaContext,
+        element: UElement,
+        overridingMethod: PsiMethod,
+        overriddenMethod: PsiMethod,
+        checkEquivalence: Boolean = true
+    ) {
+        val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION)
+        val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION)
+        val location = context.getLocation(element)
+        val overridingClass = overridingMethod.parent as PsiClass
+        val overriddenClass = overriddenMethod.parent as PsiClass
+        val overridingName = "${overridingClass.name}.${overridingMethod.name}"
+        val overriddenName = "${overriddenClass.name}.${overriddenMethod.name}"
+        if (overridingAnnotation == null) {
+            val msg = "The method $overridingName overrides the method $overriddenName which " +
+                "is annotated with @EnforcePermission. The same annotation must be used " +
+                "on $overridingName"
+            context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+        } else if (overriddenAnnotation == null) {
+            val msg = "The method $overridingName overrides the method $overriddenName which " +
+                "is not annotated with @EnforcePermission. The same annotation must be " +
+                "used on $overriddenName. Did you forget to annotate the AIDL definition?"
+            context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+        } else if (checkEquivalence && !areAnnotationsEquivalent(
+                    context, overridingAnnotation, overriddenAnnotation)) {
+            val msg = "The method $overridingName is annotated with " +
+                "${overridingAnnotation.text} which differs from the overridden " +
+                "method $overriddenName: ${overriddenAnnotation.text}. The same " +
+                "annotation must be used for both methods."
+            context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+        }
+    }
+
+    private fun compareClasses(
+        context: JavaContext,
+        element: UElement,
+        newClass: PsiClass,
+        extendedClass: PsiClass,
+        checkEquivalence: Boolean = true
+    ) {
+        val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION)
+        val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION)
+
+        val location = context.getLocation(element)
+        val newClassName = newClass.qualifiedName
+        val extendedClassName = extendedClass.qualifiedName
+        if (newAnnotation == null) {
+            val msg = "The class $newClassName extends the class $extendedClassName which " +
+                "is annotated with @EnforcePermission. The same annotation must be used " +
+                "on $newClassName."
+            context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+        } else if (extendedAnnotation == null) {
+            val msg = "The class $newClassName extends the class $extendedClassName which " +
+                "is not annotated with @EnforcePermission. The same annotation must be used " +
+                "on $extendedClassName. Did you forget to annotate the AIDL definition?"
+            context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+        } else if (checkEquivalence && !areAnnotationsEquivalent(
+            context, newAnnotation, extendedAnnotation)) {
+            val msg = "The class $newClassName is annotated with ${newAnnotation.text} " +
+                "which differs from the parent class $extendedClassName: " +
+                "${extendedAnnotation.text}. The same annotation must be used for " +
+                "both classes."
+            context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+        }
+    }
+
     override fun visitAnnotationUsage(
         context: JavaContext,
         element: UElement,
@@ -83,48 +168,42 @@
         if (usageInfo.type == AnnotationUsageType.EXTENDS) {
             val newClass = element.sourcePsi?.parent?.parent as PsiClass
             val extendedClass: PsiClass = usageInfo.referenced as PsiClass
-            val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION)
-            val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION)!!
-
-            val location = context.getLocation(element)
-            val newClassName = newClass.qualifiedName
-            val extendedClassName = extendedClass.qualifiedName
-            if (newAnnotation == null) {
-                val msg = "The class $newClassName extends the class $extendedClassName which " +
-                    "is annotated with @EnforcePermission. The same annotation must be used " +
-                    "on $newClassName."
-                context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
-            } else if (!areAnnotationsEquivalent(context, newAnnotation, extendedAnnotation)) {
-                val msg = "The class $newClassName is annotated with ${newAnnotation.text} " +
-                    "which differs from the parent class $extendedClassName: " +
-                    "${extendedAnnotation.text}. The same annotation must be used for " +
-                    "both classes."
-                context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
-            }
+            compareClasses(context, element, newClass, extendedClass)
         } else if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE &&
             annotationInfo.origin == AnnotationOrigin.METHOD) {
             val overridingMethod = element.sourcePsi as PsiMethod
             val overriddenMethod = usageInfo.referenced as PsiMethod
-            val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION)
-            val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION)!!
+            compareMethods(context, element, overridingMethod, overriddenMethod)
+        }
+    }
 
-            val location = context.getLocation(element)
-            val overridingClass = overridingMethod.parent as PsiClass
-            val overriddenClass = overriddenMethod.parent as PsiClass
-            val overridingName = "${overridingClass.name}.${overridingMethod.name}"
-            val overriddenName = "${overriddenClass.name}.${overriddenMethod.name}"
-            if (overridingAnnotation == null) {
-                val msg = "The method $overridingName overrides the method $overriddenName which " +
-                    "is annotated with @EnforcePermission. The same annotation must be used " +
-                    "on $overridingName"
-                context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
-            } else if (!areAnnotationsEquivalent(
-                        context, overridingAnnotation, overriddenAnnotation)) {
-                val msg = "The method $overridingName is annotated with " +
-                    "${overridingAnnotation.text} which differs from the overridden " +
-                    "method $overriddenName: ${overriddenAnnotation.text}. The same " +
-                    "annotation must be used for both methods."
-                context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return object : UElementHandler() {
+            override fun visitAnnotation(node: UAnnotation) {
+                if (node.qualifiedName != ENFORCE_PERMISSION) {
+                    return
+                }
+                val method = node.uastParent as? UMethod
+                val klass = node.uastParent as? UClass
+                if (klass != null) {
+                    val newClass = klass as PsiClass
+                    val extendedClass = newClass.getSuperClass()
+                    if (extendedClass != null && extendedClass.qualifiedName != JAVA_OBJECT) {
+                        // The equivalence check can be skipped, if both classes are
+                        // annotated, it will be verified by visitAnnotationUsage.
+                        compareClasses(context, klass, newClass,
+                            extendedClass, checkEquivalence = false)
+                    }
+                } else if (method != null) {
+                    val overridingMethod = method as PsiMethod
+                    val parents = overridingMethod.findSuperMethods()
+                    for (overriddenMethod in parents) {
+                        // The equivalence check can be skipped, if both methods are
+                        // annotated, it will be verified by visitAnnotationUsage.
+                        compareMethods(context, method, overridingMethod,
+                            overriddenMethod, checkEquivalence = false)
+                    }
+                }
             }
         }
     }
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt
new file mode 100644
index 0000000..cc2ab19
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.parcel
+
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
+import com.android.tools.lint.detector.api.Location
+import com.intellij.psi.PsiCallExpression
+import com.intellij.psi.PsiClassType
+import com.intellij.psi.PsiIntersectionType
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.PsiType
+import com.intellij.psi.PsiTypeParameter
+import com.intellij.psi.PsiWildcardType
+import org.jetbrains.kotlin.utils.addToStdlib.cast
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UExpression
+import org.jetbrains.uast.UVariable
+
+/**
+ * Subclass this class and override {@link #getBoundingClass} to report an unsafe Parcel API issue
+ * with a fix that migrates towards the new safer API by appending an argument in the form of
+ * {@code com.package.ItemType.class} coming from the result of the overridden method.
+ */
+abstract class CallMigrator(
+        val method: Method,
+        private val rejects: Set<String> = emptySet(),
+) {
+    open fun report(context: JavaContext, call: UCallExpression, method: PsiMethod) {
+        val location = context.getLocation(call)
+        val itemType = getBoundingClass(context, call, method)
+        val fix = (itemType as? PsiClassType)?.let { type ->
+            getParcelFix(location, this.method.name, getArgumentSuffix(type))
+        }
+        val message = "Unsafe `Parcel.${this.method.name}()` API usage"
+        context.report(SaferParcelChecker.ISSUE_UNSAFE_API_USAGE, call, location, message, fix)
+    }
+
+    protected open fun getArgumentSuffix(type: PsiClassType) =
+            ", ${type.rawType().canonicalText}.class"
+
+    protected open fun getBoundingClass(
+            context: JavaContext,
+            call: UCallExpression,
+            method: PsiMethod,
+    ): PsiType? = null
+
+    protected fun getItemType(type: PsiType, container: String): PsiClassType? {
+        val supers = getParentTypes(type).mapNotNull { it as? PsiClassType }
+        val containerType = supers.firstOrNull { it.rawType().canonicalText == container }
+                ?: return null
+        val itemType = containerType.parameters.getOrNull(0) ?: return null
+        // TODO: Expand to other types, see PsiTypeVisitor
+        return when (itemType) {
+            is PsiClassType -> itemType
+            is PsiWildcardType -> itemType.bound as PsiClassType
+            else -> null
+        }
+    }
+
+    /**
+     * Tries to obtain the type expected by the "receiving" end given a certain {@link UExpression}.
+     *
+     * This could be an assignment, an argument passed to a method call, to a constructor call, a
+     * type cast, etc. If no receiving end is found, the type of the UExpression itself is returned.
+     */
+    protected fun getReceivingType(expression: UExpression): PsiType? {
+        val parent = expression.uastParent
+        val type = when (parent) {
+            is UCallExpression -> {
+                val i = parent.valueArguments.indexOf(expression)
+                val psiCall = parent.sourcePsi as? PsiCallExpression ?: return null
+                val typeSubstitutor = psiCall.resolveMethodGenerics().substitutor
+                val method = psiCall.resolveMethod()!!
+                method.getSignature(typeSubstitutor).parameterTypes[i]
+            }
+            is UVariable -> parent.type
+            is UExpression -> parent.getExpressionType()
+            else -> null
+        }
+        return filter(type ?: expression.getExpressionType())
+    }
+
+    private fun filter(type: PsiType?): PsiType? {
+        // It's important that PsiIntersectionType case is above the one that check the type in
+        // rejects, because for intersect types, the canonicalText is one of the terms.
+        if (type is PsiIntersectionType) {
+            return type.conjuncts.mapNotNull(this::filter).firstOrNull()
+        }
+        if (type == null || type.canonicalText in rejects) {
+            return null
+        }
+        if (type is PsiClassType && type.resolve() is PsiTypeParameter) {
+            return null
+        }
+        return type
+    }
+
+    private fun getParentTypes(type: PsiType): Set<PsiType> =
+            type.superTypes.flatMap(::getParentTypes).toSet() + type
+
+    protected fun getParcelFix(location: Location, method: String, arguments: String) =
+            LintFix
+                    .create()
+                    .name("Migrate to safer Parcel.$method() API")
+                    .replace()
+                    .range(location)
+                    .pattern("$method\\s*\\(((?:.|\\n)*)\\)")
+                    .with("\\k<1>$arguments")
+                    .autoFix()
+                    .build()
+}
+
+/**
+ * This class derives the type to be appended by inferring the generic type of the {@code container}
+ * type (eg. "java.util.List") of the {@code argument}-th argument.
+ */
+class ContainerArgumentMigrator(
+        method: Method,
+        private val argument: Int,
+        private val container: String,
+        rejects: Set<String> = emptySet(),
+) : CallMigrator(method, rejects) {
+    override fun getBoundingClass(
+            context: JavaContext, call: UCallExpression, method: PsiMethod
+    ): PsiType? {
+        val firstParamType = call.valueArguments[argument].getExpressionType() ?: return null
+        return getItemType(firstParamType, container)!!
+    }
+
+    /**
+     * We need to insert a casting construct in the class parameter. For example:
+     *   (Class<Foo<Bar>>) (Class<?>) Foo.class.
+     * This is needed for when the arguments of the conflict (eg. when there is List<Foo<Bar>> and
+     * class type is Class<Foo?).
+     */
+    override fun getArgumentSuffix(type: PsiClassType): String {
+        if (type.parameters.isNotEmpty()) {
+            val rawType = type.rawType()
+            return ", (Class<${type.canonicalText}>) (Class<?>) ${rawType.canonicalText}.class"
+        }
+        return super.getArgumentSuffix(type)
+    }
+}
+
+/**
+ * This class derives the type to be appended by inferring the generic type of the {@code container}
+ * type (eg. "java.util.List") of the return type of the method.
+ */
+class ContainerReturnMigrator(
+        method: Method,
+        private val container: String,
+        rejects: Set<String> = emptySet(),
+) : CallMigrator(method, rejects) {
+    override fun getBoundingClass(
+            context: JavaContext, call: UCallExpression, method: PsiMethod
+    ): PsiType? {
+        val type = getReceivingType(call.uastParent as UExpression) ?: return null
+        return getItemType(type, container)
+    }
+}
+
+/**
+ * This class derives the type to be appended by inferring the expected type for the method result.
+ */
+class ReturnMigrator(
+        method: Method,
+        rejects: Set<String> = emptySet(),
+) : CallMigrator(method, rejects) {
+    override fun getBoundingClass(
+            context: JavaContext, call: UCallExpression, method: PsiMethod
+    ): PsiType? {
+        return getReceivingType(call.uastParent as UExpression)
+    }
+}
+
+/**
+ * This class appends the class loader and the class object by deriving the type from the method
+ * result.
+ */
+class ReturnMigratorWithClassLoader(
+        method: Method,
+        rejects: Set<String> = emptySet(),
+) : CallMigrator(method, rejects) {
+    override fun getBoundingClass(
+            context: JavaContext, call: UCallExpression, method: PsiMethod
+    ): PsiType? {
+        return getReceivingType(call.uastParent as UExpression)
+    }
+
+    override fun getArgumentSuffix(type: PsiClassType): String =
+            "${type.rawType().canonicalText}.class.getClassLoader(), " +
+                    "${type.rawType().canonicalText}.class"
+
+}
\ No newline at end of file
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/Method.kt b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/Method.kt
new file mode 100644
index 0000000..c032fa2
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/Method.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.parcel
+
+data class Method(
+    val params: List<String>,
+    val clazz: String,
+    val name: String,
+    val parameters: List<String>
+) {
+    constructor(
+        clazz: String,
+        name: String,
+        parameters: List<String>
+    ) : this(
+            listOf(), clazz, name, parameters
+    )
+
+    val signature: String
+        get() {
+            val prefix = if (params.isEmpty()) "" else "${params.joinToString(", ", "<", ">")} "
+            return "$prefix$clazz.$name(${parameters.joinToString()})"
+        }
+}
\ No newline at end of file
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt
new file mode 100644
index 0000000..89dbcae
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.parcel
+
+import com.android.tools.lint.detector.api.*
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.PsiSubstitutor
+import com.intellij.psi.PsiType
+import com.intellij.psi.PsiTypeParameter
+import org.jetbrains.uast.UCallExpression
+import java.util.*
+
+class SaferParcelChecker : Detector(), SourceCodeScanner {
+    override fun getApplicableMethodNames(): List<String> =
+            MIGRATORS
+                    .map(CallMigrator::method)
+                    .map(Method::name)
+
+    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+        if (!isAtLeastT(context)) return
+        val signature = getSignature(method)
+        val migrator = MIGRATORS.firstOrNull { it.method.signature == signature } ?: return
+        migrator.report(context, node, method)
+    }
+
+    private fun getSignature(method: PsiMethod): String {
+        val name = UastLintUtils.getQualifiedName(method)
+        val signature = method.getSignature(PsiSubstitutor.EMPTY)
+        val parameters =
+                signature.parameterTypes.joinToString(transform = PsiType::getCanonicalText)
+        val types = signature.typeParameters.map(PsiTypeParameter::getName)
+        val prefix = if (types.isEmpty()) "" else types.joinToString(", ", "<", ">") + " "
+        return "$prefix$name($parameters)"
+    }
+
+    /** Taken from androidx-main:core/core/src/main/java/androidx/core/os/BuildCompat.java */
+    private fun isAtLeastT(context: Context): Boolean {
+        val project = if (context.isGlobalAnalysis()) context.mainProject else context.project
+        return project.isAndroidProject
+                && project.minSdkVersion.featureLevel >= 32
+                && isAtLeastPreReleaseCodename("Tiramisu", project.minSdkVersion.codename)
+    }
+
+    /** Taken from androidx-main:core/core/src/main/java/androidx/core/os/BuildCompat.java */
+    private fun isAtLeastPreReleaseCodename(min: String, actual: String): Boolean {
+        if (actual == "REL") return false
+        return actual.uppercase(Locale.ROOT) >= min.uppercase(Locale.ROOT)
+    }
+
+    companion object {
+        @JvmField
+        val ISSUE_UNSAFE_API_USAGE: Issue = Issue.create(
+                id = "UnsafeParcelApi",
+                briefDescription = "Use of unsafe Parcel API",
+                explanation = """
+                    You are using a deprecated Parcel API that doesn't accept the expected class as\
+                     a parameter. This means that unexpected classes could be instantiated and\
+                     unexpected code executed.
+
+                    Please migrate to the safer alternative that takes an extra Class<T> parameter.
+                    """,
+                category = Category.SECURITY,
+                priority = 8,
+                severity = Severity.WARNING,
+
+                implementation = Implementation(
+                        SaferParcelChecker::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                )
+        )
+
+        private val METHOD_READ_SERIALIZABLE = Method("android.os.Parcel", "readSerializable", listOf())
+        private val METHOD_READ_ARRAY_LIST = Method("android.os.Parcel", "readArrayList", listOf("java.lang.ClassLoader"))
+        private val METHOD_READ_LIST = Method("android.os.Parcel", "readList", listOf("java.util.List", "java.lang.ClassLoader"))
+        private val METHOD_READ_PARCELABLE = Method(listOf("T"), "android.os.Parcel", "readParcelable", listOf("java.lang.ClassLoader"))
+        private val METHOD_READ_PARCELABLE_LIST = Method(listOf("T"), "android.os.Parcel", "readParcelableList", listOf("java.util.List<T>", "java.lang.ClassLoader"))
+        private val METHOD_READ_SPARSE_ARRAY = Method(listOf("T"), "android.os.Parcel", "readSparseArray", listOf("java.lang.ClassLoader"))
+
+        // TODO: Write migrators for methods below
+        private val METHOD_READ_ARRAY = Method("android.os.Parcel", "readArray", listOf("java.lang.ClassLoader"))
+        private val METHOD_READ_PARCELABLE_ARRAY = Method("android.os.Parcel", "readParcelableArray", listOf("java.lang.ClassLoader"))
+        private val METHOD_READ_PARCELABLE_CREATOR = Method("android.os.Parcel", "readParcelableCreator", listOf("java.lang.ClassLoader"))
+
+        private val MIGRATORS = listOf(
+                ReturnMigrator(METHOD_READ_PARCELABLE, setOf("android.os.Parcelable")),
+                ContainerArgumentMigrator(METHOD_READ_LIST, 0, "java.util.List"),
+                ContainerReturnMigrator(METHOD_READ_ARRAY_LIST, "java.util.Collection"),
+                ContainerReturnMigrator(METHOD_READ_SPARSE_ARRAY, "android.util.SparseArray"),
+                ContainerArgumentMigrator(METHOD_READ_PARCELABLE_LIST, 0, "java.util.List"),
+                ReturnMigratorWithClassLoader(METHOD_READ_SERIALIZABLE),
+        )
+    }
+}
\ No newline at end of file
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
index f5f4ebe..2cfc3fb 100644
--- a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
@@ -147,14 +147,57 @@
 1 errors, 0 warnings""".addLineContinuation())
     }
 
+    fun testDetectIssuesExtraAnnotationMethod() {
+        lint().files(java(
+            """
+            package test.pkg;
+            public class TestClass7 extends IBar.Stub {
+                @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expect("""src/test/pkg/TestClass7.java:4: Error: The method TestClass7.testMethod \
+overrides the method Stub.testMethod which is not annotated with @EnforcePermission. The same \
+annotation must be used on Stub.testMethod. Did you forget to annotate the AIDL definition? \
+[MissingEnforcePermissionAnnotation]
+    public void testMethod() {}
+                ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+    }
+
+    fun testDetectIssuesExtraAnnotationInterface() {
+        lint().files(java(
+            """
+            package test.pkg;
+            @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+            public class TestClass8 extends IBar.Stub {
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expect("""src/test/pkg/TestClass8.java:2: Error: The class test.pkg.TestClass8 \
+extends the class IBar.Stub which is not annotated with @EnforcePermission. The same annotation \
+must be used on IBar.Stub. Did you forget to annotate the AIDL definition? \
+[MissingEnforcePermissionAnnotation]
+@android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+^
+1 errors, 0 warnings""".addLineContinuation())
+    }
+
     /* Stubs */
 
+    // A service with permission annotation on the class.
     private val interfaceIFooStub: TestFile = java(
         """
         @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
         public interface IFoo {
          @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
-         public static abstract class Stub implements IFoo {
+         public static abstract class Stub extends android.os.Binder implements IFoo {
            @Override
            public void testMethod() {}
          }
@@ -163,10 +206,11 @@
         """
     ).indented()
 
+    // A service with permission annotation on the method.
     private val interfaceIFooMethodStub: TestFile = java(
         """
         public interface IFooMethod {
-         public static abstract class Stub implements IFooMethod {
+         public static abstract class Stub extends android.os.Binder implements IFooMethod {
             @Override
             @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
             public void testMethod() {}
@@ -177,6 +221,19 @@
         """
     ).indented()
 
+    // A service without any permission annotation.
+    private val interfaceIBarStub: TestFile = java(
+        """
+        public interface IBar {
+         public static abstract class Stub extends android.os.Binder implements IBar {
+            @Override
+            public void testMethod() {}
+          }
+          public void testMethod();
+        }
+        """
+    ).indented()
+
     private val manifestPermissionStub: TestFile = java(
         """
         package android.Manifest;
@@ -194,7 +251,7 @@
         """
     ).indented()
 
-    private val stubs = arrayOf(interfaceIFooStub, interfaceIFooMethodStub,
+    private val stubs = arrayOf(interfaceIFooStub, interfaceIFooMethodStub, interfaceIBarStub,
             manifestPermissionStub, enforcePermissionAnnotationStub)
 
     // Substitutes "backslash + new line" with an empty string to imitate line continuation
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt
new file mode 100644
index 0000000..05c7850
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.parcel
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.checks.infrastructure.TestMode
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class SaferParcelCheckerTest : LintDetectorTest() {
+    override fun getDetector(): Detector = SaferParcelChecker()
+
+    override fun getIssues(): List<Issue> = listOf(
+            SaferParcelChecker.ISSUE_UNSAFE_API_USAGE
+    )
+
+    override fun lint(): TestLintTask =
+            super.lint()
+                    .allowMissingSdk(true)
+                    // We don't do partial analysis in the platform
+                    .skipTestModes(TestMode.PARTIAL)
+
+    fun testDetectUnsafeReadSerializable() {
+        lint()
+                .files(
+                        java(
+                                """
+                        package test.pkg;
+                        import android.os.Parcel;
+                        import java.io.Serializable;
+
+                        public class TestClass {
+                            private TestClass(Parcel p) {
+                                Serializable ans = p.readSerializable();
+                            }
+                        }
+                        """
+                        ).indented(),
+                        *includes
+                )
+                .expectIdenticalTestModeOutput(false)
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass.java:7: Warning: Unsafe Parcel.readSerializable() \
+                        API usage [UnsafeParcelApi]
+                                Serializable ans = p.readSerializable();
+                                                   ~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 1 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDoesNotDetectSafeReadSerializable() {
+        lint()
+                .files(
+                        java(
+                                """
+                        package test.pkg;
+                        import android.os.Parcel;
+                        import java.io.Serializable;
+
+                        public class TestClass {
+                            private TestClass(Parcel p) {
+                                String ans = p.readSerializable(null, String.class);
+                            }
+                        }
+                        """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect("No warnings.")
+    }
+
+    fun testDetectUnsafeReadArrayList() {
+        lint()
+                .files(
+                        java(
+                                """
+                        package test.pkg;
+                        import android.os.Parcel;
+
+                        public class TestClass {
+                            private TestClass(Parcel p) {
+                                ArrayList ans = p.readArrayList(null);
+                            }
+                        }
+                        """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass.java:6: Warning: Unsafe Parcel.readArrayList() API \
+                        usage [UnsafeParcelApi]
+                                ArrayList ans = p.readArrayList(null);
+                                                ~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 1 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDoesNotDetectSafeReadArrayList() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        ArrayList<Intent> ans = p.readArrayList(null, Intent.class);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect("No warnings.")
+    }
+
+    fun testDetectUnsafeReadList() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+                                import java.util.List;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        List<Intent> list = new ArrayList<Intent>();
+                                        p.readList(list, null);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass.java:9: Warning: Unsafe Parcel.readList() API usage \
+                        [UnsafeParcelApi]
+                                p.readList(list, null);
+                                ~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 1 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDoesNotDetectSafeReadList() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+                                import java.util.List;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        List<Intent> list = new ArrayList<Intent>();
+                                        p.readList(list, null, Intent.class);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect("No warnings.")
+    }
+
+    fun testDetectUnsafeReadParcelable() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        Intent ans = p.readParcelable(null);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass.java:7: Warning: Unsafe Parcel.readParcelable() API \
+                        usage [UnsafeParcelApi]
+                                Intent ans = p.readParcelable(null);
+                                             ~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 1 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDoesNotDetectSafeReadParcelable() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        Intent ans = p.readParcelable(null, Intent.class);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect("No warnings.")
+    }
+
+    fun testDetectUnsafeReadParcelableList() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+                                import java.util.List;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        List<Intent> list = new ArrayList<Intent>();
+                                        List<Intent> ans = p.readParcelableList(list, null);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass.java:9: Warning: Unsafe Parcel.readParcelableList() \
+                        API usage [UnsafeParcelApi]
+                                List<Intent> ans = p.readParcelableList(list, null);
+                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 1 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDoesNotDetectSafeReadParcelableList() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+                                import java.util.List;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        List<Intent> list = new ArrayList<Intent>();
+                                        List<Intent> ans =
+                                                p.readParcelableList(list, null, Intent.class);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect("No warnings.")
+    }
+
+    fun testDetectUnsafeReadSparseArray() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+                                import android.util.SparseArray;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        SparseArray<Intent> ans = p.readSparseArray(null);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass.java:8: Warning: Unsafe Parcel.readSparseArray() API\
+                         usage [UnsafeParcelApi]
+                                SparseArray<Intent> ans = p.readSparseArray(null);
+                                                          ~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 1 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDoesNotDetectSafeReadSparseArray() {
+        lint()
+                .files(
+                        java(
+                                """
+                                package test.pkg;
+                                import android.content.Intent;
+                                import android.os.Parcel;
+                                import android.util.SparseArray;
+
+                                public class TestClass {
+                                    private TestClass(Parcel p) {
+                                        SparseArray<Intent> ans =
+                                                p.readSparseArray(null, Intent.class);
+                                    }
+                                }
+                                """
+                        ).indented(),
+                        *includes
+                )
+                .run()
+                .expect("No warnings.")
+    }
+
+    /** Stubs for classes used for testing */
+
+
+    private val includes =
+            arrayOf(
+                manifest().minSdk("Tiramisu"),
+                java(
+                        """
+                        package android.os;
+                        import java.util.ArrayList;
+                        import java.util.List;
+                        import java.util.Map;
+                        import java.util.HashMap;
+
+                        public final class Parcel {
+                            // Deprecateds
+                            public Object[] readArray(ClassLoader loader) { return null; }
+                            public ArrayList readArrayList(ClassLoader loader) { return null; }
+                            public HashMap readHashMap(ClassLoader loader) { return null; }
+                            public void readList(List outVal, ClassLoader loader) {}
+                            public void readMap(Map outVal, ClassLoader loader) {}
+                            public <T extends Parcelable> T readParcelable(ClassLoader loader) { return null; }
+                            public Parcelable[] readParcelableArray(ClassLoader loader) { return null; }
+                            public Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) { return null; }
+                            public <T extends Parcelable> List<T> readParcelableList(List<T> list, ClassLoader cl) { return null; }
+                            public Serializable readSerializable() { return null; }
+                            public <T> SparseArray<T> readSparseArray(ClassLoader loader) { return null; }
+
+                            // Replacements
+                            public <T> T[] readArray(ClassLoader loader, Class<T> clazz) { return null; }
+                            public <T> ArrayList<T> readArrayList(ClassLoader loader, Class<? extends T> clazz) { return null; }
+                            public <K, V> HashMap<K,V> readHashMap(ClassLoader loader, Class<? extends K> clazzKey, Class<? extends V> clazzValue) { return null; }
+                            public <T> void readList(List<? super T> outVal, ClassLoader loader, Class<T> clazz) {}
+                            public <K, V> void readMap(Map<? super K, ? super V> outVal, ClassLoader loader, Class<K> clazzKey, Class<V> clazzValue) {}
+                            public <T> T readParcelable(ClassLoader loader, Class<T> clazz) { return null; }
+                            public <T> T[] readParcelableArray(ClassLoader loader, Class<T> clazz) { return null; }
+                            public <T> Parcelable.Creator<T> readParcelableCreator(ClassLoader loader, Class<T> clazz) { return null; }
+                            public <T> List<T> readParcelableList(List<T> list, ClassLoader cl, Class<T> clazz) { return null; }
+                            public <T> T readSerializable(ClassLoader loader, Class<T> clazz) { return null; }
+                            public <T> SparseArray<T> readSparseArray(ClassLoader loader, Class<? extends T> clazz) { return null; }
+                        }
+                        """
+                ).indented(),
+                java(
+                        """
+                        package android.os;
+                        public interface Parcelable {}
+                        """
+                ).indented(),
+                java(
+                        """
+                        package android.content;
+                        public class Intent implements Parcelable, Cloneable {}
+                        """
+                ).indented(),
+                java(
+                        """
+                        package android.util;
+                        public class SparseArray<E> implements Cloneable {}
+                        """
+                ).indented(),
+            )
+
+    // Substitutes "backslash + new line" with an empty string to imitate line continuation
+    private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}
diff --git a/tools/preload-check/device/src/com/android/preload/check/Util.java b/tools/preload-check/device/src/com/android/preload/check/Util.java
index fccea0a0..f521c95 100644
--- a/tools/preload-check/device/src/com/android/preload/check/Util.java
+++ b/tools/preload-check/device/src/com/android/preload/check/Util.java
@@ -25,8 +25,8 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.LinkedList;
 import java.util.List;
 
 public class Util {
@@ -48,7 +48,7 @@
         Class<?> vmClassLoaderClass = Class.forName("java.lang.VMClassLoader");
         Method getResources = vmClassLoaderClass.getDeclaredMethod("getResources", String.class);
         getResources.setAccessible(true);
-        LinkedList<DexFile> res = new LinkedList<>();
+        ArrayList<DexFile> res = new ArrayList<>();
         for (int i = 1;; i++) {
             try {
                 String name = "classes" + (i > 1 ? String.valueOf(i) : "") + ".dex";