Merge "MediaUtils: Tag HAL_PIXEL_FORMAT_RAW12 as non-YUV"
diff --git a/Android.bp b/Android.bp
index 00b4198..3d087c0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -433,11 +433,8 @@
         "core/java/android/util/LocalLog.java",
         "core/java/com/android/internal/util/HexDump.java",
         "core/java/com/android/internal/util/IndentingPrintWriter.java",
-        "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/MessageUtils.java",
         "core/java/com/android/internal/util/RingBufferIndices.java",
-        "core/java/com/android/internal/util/State.java",
-        "core/java/com/android/internal/util/StateMachine.java",
         "core/java/com/android/internal/util/WakeupMessage.java",
         "core/java/com/android/internal/util/TokenBucket.java",
     ],
@@ -566,6 +563,7 @@
         "android.hardware.vibrator-V1.3-java",
         "framework-protos",
         "art.module.public.api",
+        "sdk_module-lib_current_framework-tethering",
         // There are a few classes from modules used by the core that
         // need to be resolved by metalava. We use a prebuilt stub of the
         // full sdk to ensure we can resolve them. If a new class gets added,
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 44c55c2..d090296 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -318,6 +318,7 @@
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":module-lib-api-stubs-docs-non-updatable"],
     libs: [
+        "sdk_module-lib_current_framework-tethering",
         "sdk_system_current_android",
         // NOTE: The below can be removed once the prebuilt stub contains IKE.
         "sdk_system_current_android.net.ipsec.ike",
diff --git a/config/README.md b/config/README.md
index 5597ae2..450a5c6 100644
--- a/config/README.md
+++ b/config/README.md
@@ -1,7 +1,7 @@
 # Configuration files for ART compiling the framework
 
 * boot-image-profile.txt: A list of methods from the boot classpath to be compiled by dex2oat.
-  The order in the file is not relelvant.
+  The order in the file is not relevant.
 * boot-profile.txt: An ordered list of methods from the boot classpath to be compiled by
   the JIT in the order provided in the file. Used by JIT zygote, when on-device
   signing failed.
diff --git a/core/api/current.txt b/core/api/current.txt
index 5b92db8..f21518c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40704,6 +40704,7 @@
     method @NonNull public java.util.List<java.lang.Integer> getBands();
     method @NonNull public java.util.List<java.lang.String> getMccMncs();
     method public int getPriority();
+    method @NonNull public java.util.List<android.telephony.RadioAccessSpecifier> getRadioAccessSpecifiers();
     method public int getSubId();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.AvailableNetworkInfo> CREATOR;
@@ -40712,6 +40713,14 @@
     field public static final int PRIORITY_MED = 2; // 0x2
   }
 
+  public static final class AvailableNetworkInfo.Builder {
+    ctor public AvailableNetworkInfo.Builder(int);
+    method @NonNull public android.telephony.AvailableNetworkInfo build();
+    method @NonNull public android.telephony.AvailableNetworkInfo.Builder setMccMncs(@NonNull java.util.List<java.lang.String>);
+    method @NonNull public android.telephony.AvailableNetworkInfo.Builder setPriority(int);
+    method @NonNull public android.telephony.AvailableNetworkInfo.Builder setRadioAccessSpecifiers(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
+  }
+
   public final class BarringInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.telephony.BarringInfo.BarringServiceInfo getBarringServiceInfo(int);
@@ -40820,6 +40829,7 @@
     field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
     field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
     field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+    field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
     field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
     field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
     field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
@@ -40881,6 +40891,8 @@
     field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
     field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int";
+    field public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT = "esim_download_retry_backoff_timer_sec_int";
+    field public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT = "esim_max_download_retry_attempts_int";
     field public static final String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
     field public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
@@ -40983,6 +40995,7 @@
     field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
     field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
     field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+    field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
     field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
     field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
@@ -47531,6 +47544,10 @@
     field public static final int KEYCODE_CUT = 277; // 0x115
     field public static final int KEYCODE_D = 32; // 0x20
     field public static final int KEYCODE_DEL = 67; // 0x43
+    field public static final int KEYCODE_DEMO_APP_1 = 301; // 0x12d
+    field public static final int KEYCODE_DEMO_APP_2 = 302; // 0x12e
+    field public static final int KEYCODE_DEMO_APP_3 = 303; // 0x12f
+    field public static final int KEYCODE_DEMO_APP_4 = 304; // 0x130
     field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
     field public static final int KEYCODE_DPAD_DOWN = 20; // 0x14
     field public static final int KEYCODE_DPAD_DOWN_LEFT = 269; // 0x10d
@@ -47562,6 +47579,10 @@
     field public static final int KEYCODE_F7 = 137; // 0x89
     field public static final int KEYCODE_F8 = 138; // 0x8a
     field public static final int KEYCODE_F9 = 139; // 0x8b
+    field public static final int KEYCODE_FEATURED_APP_1 = 297; // 0x129
+    field public static final int KEYCODE_FEATURED_APP_2 = 298; // 0x12a
+    field public static final int KEYCODE_FEATURED_APP_3 = 299; // 0x12b
+    field public static final int KEYCODE_FEATURED_APP_4 = 300; // 0x12c
     field public static final int KEYCODE_FOCUS = 80; // 0x50
     field public static final int KEYCODE_FORWARD = 125; // 0x7d
     field public static final int KEYCODE_FORWARD_DEL = 112; // 0x70
@@ -47727,6 +47748,14 @@
     field public static final int KEYCODE_U = 49; // 0x31
     field public static final int KEYCODE_UNKNOWN = 0; // 0x0
     field public static final int KEYCODE_V = 50; // 0x32
+    field public static final int KEYCODE_VIDEO_APP_1 = 289; // 0x121
+    field public static final int KEYCODE_VIDEO_APP_2 = 290; // 0x122
+    field public static final int KEYCODE_VIDEO_APP_3 = 291; // 0x123
+    field public static final int KEYCODE_VIDEO_APP_4 = 292; // 0x124
+    field public static final int KEYCODE_VIDEO_APP_5 = 293; // 0x125
+    field public static final int KEYCODE_VIDEO_APP_6 = 294; // 0x126
+    field public static final int KEYCODE_VIDEO_APP_7 = 295; // 0x127
+    field public static final int KEYCODE_VIDEO_APP_8 = 296; // 0x128
     field public static final int KEYCODE_VOICE_ASSIST = 231; // 0xe7
     field public static final int KEYCODE_VOLUME_DOWN = 25; // 0x19
     field public static final int KEYCODE_VOLUME_MUTE = 164; // 0xa4
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9af6c1b..3a35f24 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -113,6 +113,7 @@
   public class AudioManager {
     method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
     method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+    method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo);
     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);
@@ -122,6 +123,19 @@
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public final class BtProfileConnectionInfo implements android.os.Parcelable {
+    method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int);
+    method public int describeContents();
+    method public boolean getIsLeOutput();
+    method public int getProfile();
+    method public boolean getSuppressNoisyIntent();
+    method public int getVolume();
+    method @NonNull public static android.media.BtProfileConnectionInfo hearingAidInfo(boolean);
+    method @NonNull public static android.media.BtProfileConnectionInfo leAudio(boolean, boolean);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.BtProfileConnectionInfo> CREATOR;
+  }
+
   public class MediaMetadataRetriever implements java.lang.AutoCloseable {
     field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6ccdf91..d0e659b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2748,7 +2748,7 @@
     method public static String actionToString(int);
     method public final void setDisplayId(int);
     field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
-    field public static final int LAST_KEYCODE = 288; // 0x120
+    field public static final int LAST_KEYCODE = 304; // 0x130
   }
 
   public final class KeyboardShortcutGroup implements android.os.Parcelable {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index e08a493..ca9a468 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -343,7 +343,6 @@
 filegroup {
     name: "framework-telephony-common-shared-srcs",
     srcs: [
-        ":modules-utils-preconditions-srcs",
         "android/os/RegistrantList.java",
         "android/os/Registrant.java",
         "android/util/IndentingPrintWriter.java",
@@ -355,10 +354,7 @@
         "com/android/internal/util/BitwiseInputStream.java",
         "com/android/internal/util/FastXmlSerializer.java",
         "com/android/internal/util/HexDump.java",
-        "com/android/internal/util/IState.java",
         "com/android/internal/util/IndentingPrintWriter.java",
-        "com/android/internal/util/State.java",
-        "com/android/internal/util/StateMachine.java",
         "com/android/internal/util/UserIcons.java",
     ],
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index dac8ffe..cf00cbd 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2022,6 +2022,7 @@
      *                {@link BluetoothProfile#HEADSET},
      *                {@link BluetoothProfile#A2DP},
      *                {@link BluetoothProfile#HEARING_AID}
+     *                {@link BluetoothProfile#LE_AUDIO}
      * @return A list of active bluetooth devices
      * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile}
      * @hide
@@ -2034,12 +2035,14 @@
     public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) {
         if (profile != BluetoothProfile.HEADSET
                 && profile != BluetoothProfile.A2DP
-                && profile != BluetoothProfile.HEARING_AID) {
+                && profile != BluetoothProfile.HEARING_AID
+                && profile != BluetoothProfile.LE_AUDIO) {
             Log.e(TAG, "Invalid profile param value in getActiveDevices");
             throw new IllegalArgumentException("Profiles must be one of "
                     + "BluetoothProfile.A2DP, "
                     + "BluetoothProfile.HEARING_AID, or"
-                    + "BluetoothProfile.HEARING_AID");
+                    + "BluetoothProfile.HEARING_AID"
+                    + "BluetoothProfile.LE_AUDIO");
         }
         try {
             mServiceLock.readLock().lock();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d811040..e781c2f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1147,6 +1147,10 @@
      * numbers.  Applications can <strong>dial</strong> emergency numbers using
      * {@link #ACTION_DIAL}, however.
      *
+     * <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use
+     * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than
+     * relying on this intent.
+     *
      * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M}
      * and above and declares as using the {@link android.Manifest.permission#CALL_PHONE}
      * permission which is not granted, then attempting to use this action will
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 7ef5bac..8605248 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -232,11 +232,10 @@
         ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO);
         ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO);
 
-        // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
-        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
     }
 
     private static final Set<String> ENABLED_ALGOS =
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index f3a8b5d..9f3a847 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5365,5 +5365,14 @@
          */
         public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS =
                 "d2d_sharing_contacts";
+
+        /**
+        * TelephonyProvider column name for NR Advanced calling
+        * Determines if the user has enabled VoNR settings for this subscription.
+        *
+        * @hide
+        */
+        public static final String COLUMN_NR_ADVANCED_CALLING_ENABLED =
+                "nr_advanced_calling_enabled";
     }
 }
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index cda9b23..ba6f4eb 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -830,13 +830,45 @@
      * consuming content. May be consumed by system to set account globally.
      */
     public static final int KEYCODE_PROFILE_SWITCH = 288;
+    /** Key code constant: Video Application key #1. */
+    public static final int KEYCODE_VIDEO_APP_1 = 289;
+    /** Key code constant: Video Application key #2. */
+    public static final int KEYCODE_VIDEO_APP_2 = 290;
+    /** Key code constant: Video Application key #3. */
+    public static final int KEYCODE_VIDEO_APP_3 = 291;
+    /** Key code constant: Video Application key #4. */
+    public static final int KEYCODE_VIDEO_APP_4 = 292;
+    /** Key code constant: Video Application key #5. */
+    public static final int KEYCODE_VIDEO_APP_5 = 293;
+    /** Key code constant: Video Application key #6. */
+    public static final int KEYCODE_VIDEO_APP_6 = 294;
+    /** Key code constant: Video Application key #7. */
+    public static final int KEYCODE_VIDEO_APP_7 = 295;
+    /** Key code constant: Video Application key #8. */
+    public static final int KEYCODE_VIDEO_APP_8 = 296;
+    /** Key code constant: Featured Application key #1. */
+    public static final int KEYCODE_FEATURED_APP_1 = 297;
+    /** Key code constant: Featured Application key #2. */
+    public static final int KEYCODE_FEATURED_APP_2 = 298;
+    /** Key code constant: Featured Application key #3. */
+    public static final int KEYCODE_FEATURED_APP_3 = 299;
+    /** Key code constant: Featured Application key #4. */
+    public static final int KEYCODE_FEATURED_APP_4 = 300;
+    /** Key code constant: Demo Application key #1. */
+    public static final int KEYCODE_DEMO_APP_1 = 301;
+    /** Key code constant: Demo Application key #2. */
+    public static final int KEYCODE_DEMO_APP_2 = 302;
+    /** Key code constant: Demo Application key #3. */
+    public static final int KEYCODE_DEMO_APP_3 = 303;
+    /** Key code constant: Demo Application key #4. */
+    public static final int KEYCODE_DEMO_APP_4 = 304;
 
-    /**
+   /**
      * Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent.
      * @hide
      */
     @TestApi
-    public static final int LAST_KEYCODE = KEYCODE_PROFILE_SWITCH;
+    public static final int LAST_KEYCODE = KEYCODE_DEMO_APP_4;
 
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 8143cf9..ffce461 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -157,6 +157,7 @@
     private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats);
 
     private static native long[] nativeGetPhysicalDisplayIds();
+    private static native long nativeGetPrimaryPhysicalDisplayId();
     private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
     private static native IBinder nativeCreateDisplay(String name, boolean secure);
     private static native void nativeDestroyDisplay(IBinder displayToken);
@@ -2266,6 +2267,15 @@
     }
 
     /**
+     * Exposed to identify the correct display to apply the primary display orientation. Avoid using
+     * for any other purpose.
+     * @hide
+     */
+    public static long getPrimaryPhysicalDisplayId() {
+        return nativeGetPrimaryPhysicalDisplayId();
+    }
+
+    /**
      * @hide
      */
     public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8d12df22..e477183 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -889,6 +889,12 @@
     return array;
 }
 
+static jlong nativeGetPrimaryPhysicalDisplayId(JNIEnv* env, jclass clazz) {
+    PhysicalDisplayId displayId;
+    SurfaceComposerClient::getPrimaryPhysicalDisplayId(&displayId);
+    return static_cast<jlong>(displayId.value);
+}
+
 static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
     sp<IBinder> token =
             SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId));
@@ -1879,6 +1885,8 @@
             (void*)nativeReleaseFrameRateFlexibilityToken },
     {"nativeGetPhysicalDisplayIds", "()[J",
             (void*)nativeGetPhysicalDisplayIds },
+    {"nativeGetPrimaryPhysicalDisplayId", "()J",
+            (void*)nativeGetPrimaryPhysicalDisplayId },
     {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
             (void*)nativeGetPhysicalDisplayToken },
     {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;",
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a5f5051..dc92e10 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1984,6 +1984,22 @@
         <enum name="KEYCODE_THUMBS_UP" value="286" />
         <enum name="KEYCODE_THUMBS_DOWN" value="287" />
         <enum name="KEYCODE_PROFILE_SWITCH" value="288" />
+        <enum name="KEYCODE_VIDEO_APP_1" value="289" />
+        <enum name="KEYCODE_VIDEO_APP_2" value="290" />
+        <enum name="KEYCODE_VIDEO_APP_3" value="291" />
+        <enum name="KEYCODE_VIDEO_APP_4" value="292" />
+        <enum name="KEYCODE_VIDEO_APP_5" value="293" />
+        <enum name="KEYCODE_VIDEO_APP_6" value="294" />
+        <enum name="KEYCODE_VIDEO_APP_7" value="295" />
+        <enum name="KEYCODE_VIDEO_APP_8" value="296" />
+        <enum name="KEYCODE_FEATURED_APP_1" value="297" />
+        <enum name="KEYCODE_FEATURED_APP_2" value="298" />
+        <enum name="KEYCODE_FEATURED_APP_3" value="299" />
+        <enum name="KEYCODE_FEATURED_APP_4" value="300" />
+        <enum name="KEYCODE_DEMO_APP_1" value="301" />
+        <enum name="KEYCODE_DEMO_APP_2" value="302" />
+        <enum name="KEYCODE_DEMO_APP_3" value="303" />
+        <enum name="KEYCODE_DEMO_APP_4" value="304" />
     </attr>
 
     <!-- ***************************************************************** -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a350d14..be5063f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -64,16 +64,74 @@
     <!-- Displayed when a carrier does not support call forwarding queries when roaming. -->
     <string name="mmiErrorWhileRoaming">Can not change call forwarding settings from your phone while you are roaming.</string>
 
-    <!-- Displayed when a phone feature such as call barring was activated. -->
+    <!-- Displayed when a phone feature such as call forwarding, call waiting, or call barring was
+         activated.
+         Used to build messages of the form:
+         <X>
+         <Y>
+
+         Where <X> is the name of the service which was enabled.  Can be one of:
+         {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding},
+         {@link #PwdMmi Password change},  {@link #CwMmi Call waiting},
+         {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID},
+         {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}.
+         And <Y> is {@link #serviceEnabled} (this string).
+          -->
     <string name="serviceEnabled">Service was enabled.</string>
     <!-- Displayed in front of the list of a set of service classes
-         (voice, data, fax, etc.) that were enabled. -->
+         (voice, data, fax, etc.) that call waiting were enabled for.
+         Will be used with messages of the form:
+         <X>
+         <Y1>
+         ...
+         <Yn>
+         Where <X> is {@link #serviceEnabledFor} (this string) and <Y>..<Yn> can be:
+         {@link #serviceClassData}, {@link #serviceClassVoice}, {@link #serviceClassFAX},
+         {@link #serviceClassSMS}, {@link #serviceClassDataAsync}, {@link #serviceClassDataSync},
+         {@link #serviceClassPacket}, {@link #serviceClassPAD}.
+         -->
     <string name="serviceEnabledFor">Service was enabled for:</string>
-    <!-- Displayed when a phone feature such as call forwarding was deactivated. -->
+    <!-- Displayed when a phone feature such as call forwarding was deactivated.
+         Used to build messages of the form:
+         <X>
+         <Y>
+
+         Where <X> is the name of the service which was disabled.  Can be one of:
+         {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding},
+         {@link #PwdMmi Password change},  {@link #CwMmi Call waiting},
+         {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID},
+         {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}.
+         And <Y> is {@link #serviceDisabled} (this string).
+         -->
     <string name="serviceDisabled">Service has been disabled.</string>
-    <!-- Displayed when a phone property such as a SIM password was registered. -->
+    <!-- Displayed when a phone property such as a SIM password was registered.  Registration
+         entails setting up a service for use, where {@link #serviceEnabled} entails enabling a
+         previously registered service.
+         Used to build messages of the form:
+         <X>
+         <Y>
+
+         Where <X> is the name of the service which was registered.  Can be one of:
+         {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding},
+         {@link #PwdMmi Password change},  {@link #CwMmi Call waiting},
+         {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID},
+         {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}.
+         And <Y> is {@link #serviceRegistered} (this string). -->
     <string name="serviceRegistered">Registration was successful.</string>
-    <!-- Displayed when a phone property such as a SIM password was erased. -->
+    <!-- Displayed when a phone property such as a SIM password was erased.
+         Erasure is the opposite of {@link #serviceRegistered} and entails removal of a service.
+
+         Used to build messages of the form:
+         <X>
+         <Y>
+
+         Where <X> is the name of the service which was registered.  Can be one of:
+         {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding},
+         {@link #PwdMmi Password change},  {@link #CwMmi Call waiting},
+         {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID},
+         {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}.
+         And <Y> is {@link #serviceErased} (this string).
+         -->
     <string name="serviceErased">Erasure was successful.</string>
     <!-- Displayed when a SIM password was entered incorrectly. -->
     <string name="passwordIncorrect">Incorrect password.</string>
@@ -107,23 +165,32 @@
          [CHAR LIMIT=10] -->
     <string name="meid">MEID</string>
 
-    <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. -->
+    <!-- Displayed as the title for a success/failure report enabling/disabling caller ID.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="ClipMmi">Incoming Caller ID</string>
-    <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. -->
+    <!-- Displayed as the title for a success/failure report enabling/disabling caller ID.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="ClirMmi">Hide Outgoing Caller ID</string>
-    <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. -->
+    <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="ColpMmi">Connected Line ID</string>
-    <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. -->
+    <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="ColrMmi">Connected Line ID Restriction</string>
-    <!-- Displayed as the title for a success/failure report enabling/disabling call forwarding. -->
+    <!-- Displayed as the title for a success/failure report enabling/disabling call forwarding.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="CfMmi">Call forwarding</string>
-    <!-- Displayed as the title for a success/failure report enabling/disabling call waiting. -->
+    <!-- Displayed as the title for a success/failure report enabling/disabling call waiting.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="CwMmi">Call waiting</string>
-    <!-- Displayed as the title for a success/failure report enabling/disabling call barring. -->
+    <!-- Displayed as the title for a success/failure report enabling/disabling call barring.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="BaMmi">Call barring</string>
-    <!-- Displayed as the title for a success/failure report changing the SIM password. -->
+    <!-- Displayed as the title for a success/failure report changing the SIM password.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="PwdMmi">Password change</string>
-    <!-- Displayed as the title for a success/failure report changing the SIM PIN. -->
+    <!-- Displayed as the title for a success/failure report changing the SIM PIN.
+         See {@link #serviceEnabled}, {@link #serviceDisabled}. -->
     <string name="PinMmi">PIN change</string>
     <string name="CnipMmi">Calling number present</string>
     <string name="CnirMmi">Calling number restricted</string>
@@ -198,21 +265,29 @@
     <string name="peerTtyModeOff">Peer requested TTY Mode OFF</string>
 
     <!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip />
-    <!-- Example: Service was enabled for: Voice, Data -->
+    <!-- Example: Service was enabled for: Voice, Data
+         See {@link #serviceEnabledFor}.-->
     <string name="serviceClassVoice">Voice</string>
-    <!-- Example: Service was enabled for: Voice, Data -->
+    <!-- Example: Service was enabled for: Voice, Data.
+         See {@link #serviceEnabledFor}. -->
     <string name="serviceClassData">Data</string>
-    <!-- Example: Service was enabled for: Voice, FAX -->
+    <!-- Example: Service was enabled for: Voice, FAX
+         See {@link #serviceEnabledFor}. -->
     <string name="serviceClassFAX">FAX</string>
-    <!-- Example: Service was enabled for: Voice, SMS -->
+    <!-- Example: Service was enabled for: Voice, SMS
+         See {@link #serviceEnabledFor}. -->
     <string name="serviceClassSMS">SMS</string>
-    <!-- Meaning: asynchronous data.  Example: Service was enabled for: Voice, Async -->
+    <!-- Meaning: asynchronous data.  Example: Service was enabled for: Voice, Async
+         See {@link #serviceEnabledFor}. -->
     <string name="serviceClassDataAsync">Async</string>
-    <!-- Meaning: synchronous data.  Example: Service was enabled for: Voice, Async -->
+    <!-- Meaning: synchronous data.  Example: Service was enabled for: Voice, Async
+         See {@link #serviceEnabledFor}. -->
     <string name="serviceClassDataSync">Sync</string>
-    <!-- Meaning: packet data.  Example: Service was enabled for: Voice, Packet -->
+    <!-- Meaning: packet data.  Example: Service was enabled for: Voice, Packet
+         See {@link #serviceEnabledFor}. -->
     <string name="serviceClassPacket">Packet</string>
-    <!-- Meaning: unknown.  Example: Service was enabled for: Voice, PAD -->
+    <!-- Meaning: unknown.  Example: Service was enabled for: Voice, PAD
+        See {@link #serviceEnabledFor}. -->
     <string name="serviceClassPAD">PAD</string>
 
     <!-- CDMA Roaming Indicator Strings (non ERI)--> <skip />
diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS
new file mode 100644
index 0000000..0fb0c30
--- /dev/null
+++ b/core/tests/coretests/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b67988e..c94b3d5 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -499,6 +499,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-1556507536": {
+      "message": "Passing transform hint %d for window %s%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "-1554521902": {
       "message": "showInsets(ime) was requested by different window: %s ",
       "level": "WARN",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index cf7039b..c7f5696 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -32,7 +32,6 @@
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothCodecConfig;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -5796,112 +5795,25 @@
         }
     }
 
-     /**
-     * Indicate Hearing Aid connection state change and eventually suppress
-     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
-     * This operation is asynchronous but its execution will still be sequentially scheduled
-     * relative to calls to {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-     * * BluetoothDevice, int, int, boolean, int)} and
-     * and {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}.
-     * @param device Bluetooth device connected/disconnected
-     * @param state new connection state (BluetoothProfile.STATE_xxx)
-     * @param musicDevice Default get system volume for the connecting device.
-     * (either {@link android.bluetooth.BluetoothProfile.hearingaid} or
-     * {@link android.bluetooth.BluetoothProfile.HEARING_AID})
-     * @param suppressNoisyIntent if true the
-     * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
-     * {@hide}
-     */
-    public void setBluetoothHearingAidDeviceConnectionState(
-                BluetoothDevice device, int state, boolean suppressNoisyIntent,
-                int musicDevice) {
-        final IAudioService service = getService();
-        try {
-            service.setBluetoothHearingAidDeviceConnectionState(device,
-                state, suppressNoisyIntent, musicDevice);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     /**
-    * Indicate Le Audio output device connection state change and eventually suppress
-    * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
-    * @param device Bluetooth device connected/disconnected
-    * @param state new connection state (BluetoothProfile.STATE_xxx)
-    * @param suppressNoisyIntent if true the
-    * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
-    * {@hide}
-    */
-    public void setBluetoothLeAudioOutDeviceConnectionState(BluetoothDevice device, int state,
-            boolean suppressNoisyIntent) {
-        final IAudioService service = getService();
-        try {
-            service.setBluetoothLeAudioOutDeviceConnectionState(device, state, suppressNoisyIntent);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-    * Indicate Le Audio input connection state change.
-    * @param device Bluetooth device connected/disconnected
-    * @param state new connection state (BluetoothProfile.STATE_xxx)
-    * {@hide}
-    */
-    public void setBluetoothLeAudioInDeviceConnectionState(BluetoothDevice device, int state) {
-        final IAudioService service = getService();
-        try {
-            service.setBluetoothLeAudioInDeviceConnectionState(device, state);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-     /**
-     * Indicate A2DP source or sink connection state change and eventually suppress
-     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
-     * This operation is asynchronous but its execution will still be sequentially scheduled
-     * relative to calls to {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice,
-     * int, boolean, int)} and
-     * {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}.
-     * @param device Bluetooth device connected/disconnected
-     * @param state  new connection state, {@link BluetoothProfile#STATE_CONNECTED}
-     *     or {@link BluetoothProfile#STATE_DISCONNECTED}
-     * @param profile profile for the A2DP device
-     * @param a2dpVolume New volume for the connecting device. Does nothing if disconnecting.
-     * (either {@link android.bluetooth.BluetoothProfile.A2DP} or
-     * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
-     * @param suppressNoisyIntent if true the
-     * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+     * Indicate Bluetooth profile connection state change.
+     * Configuration changes for A2DP are indicated by having the same <code>newDevice</code> and
+     * <code>previousDevice</code>
+     * This operation is asynchronous.
+     *
+     * @param newDevice Bluetooth device connected or null if there is no new devices
+     * @param previousDevice Bluetooth device disconnected or null if there is no disconnected
+     * devices
+     * @param info contain all info related to the device. {@link BtProfileConnectionInfo}
      * {@hide}
      */
-    public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-            BluetoothDevice device, int state,
-            int profile, boolean suppressNoisyIntent, int a2dpVolume) {
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+    public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice,
+            @Nullable BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) {
         final IAudioService service = getService();
         try {
-            service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
-                state, profile, suppressNoisyIntent, a2dpVolume);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-     /**
-     * Indicate A2DP device configuration has changed.
-     * This operation is asynchronous but its execution will still be sequentially scheduled
-     * relative to calls to
-     * {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice, int, int,
-     * boolean, int)} and
-     * {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, int, boolean, int)}
-     * @param device Bluetooth device whose configuration has changed.
-     * {@hide}
-     */
-    public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) {
-        final IAudioService service = getService();
-        try {
-            service.handleBluetoothA2dpDeviceConfigChange(device);
+            service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/BtProfileConnectionInfo.aidl b/media/java/android/media/BtProfileConnectionInfo.aidl
new file mode 100644
index 0000000..047f06b
--- /dev/null
+++ b/media/java/android/media/BtProfileConnectionInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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 android.media;
+
+parcelable BtProfileConnectionInfo;
+
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
new file mode 100644
index 0000000..19ea2de
--- /dev/null
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 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 android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.bluetooth.BluetoothProfile;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains information about Bluetooth profile connection state changed
+ * {@hide}
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class BtProfileConnectionInfo implements Parcelable {
+    /** @hide */
+    @IntDef({
+            BluetoothProfile.A2DP,
+            BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper
+            BluetoothProfile.HEADSET, // Can only be set by BtHelper
+            BluetoothProfile.HEARING_AID,
+            BluetoothProfile.LE_AUDIO,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BtProfile {}
+
+    private final @BtProfile int mProfile;
+    private final boolean mSupprNoisy;
+    private final int mVolume;
+    private final boolean mIsLeOutput;
+
+    private BtProfileConnectionInfo(@BtProfile int profile, boolean suppressNoisyIntent, int volume,
+            boolean isLeOutput) {
+        mProfile = profile;
+        mSupprNoisy = suppressNoisyIntent;
+        mVolume = volume;
+        mIsLeOutput = isLeOutput;
+    }
+
+    /**
+     * Constructor used by BtHelper when a profile is connected
+     * {@hide}
+     */
+    public BtProfileConnectionInfo(@BtProfile int profile) {
+        this(profile, false, -1, false);
+    }
+
+    public static final @NonNull Parcelable.Creator<BtProfileConnectionInfo> CREATOR =
+            new Parcelable.Creator<BtProfileConnectionInfo>() {
+                @Override
+                public BtProfileConnectionInfo createFromParcel(Parcel source) {
+                    return new BtProfileConnectionInfo(source.readInt(), source.readBoolean(),
+                            source.readInt(), source.readBoolean());
+                }
+
+                @Override
+                public BtProfileConnectionInfo[] newArray(int size) {
+                    return new BtProfileConnectionInfo[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
+        dest.writeInt(mProfile);
+        dest.writeBoolean(mSupprNoisy);
+        dest.writeInt(mVolume);
+        dest.writeBoolean(mIsLeOutput);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Constructor for A2dp info
+     *
+     * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
+     * intent will not be sent.
+     *
+     * @param volume of device -1 to ignore value
+     */
+    public static @NonNull BtProfileConnectionInfo a2dpInfo(boolean suppressNoisyIntent,
+            int volume) {
+        return new BtProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, volume,
+            false);
+    }
+
+    /**
+     * Constructor for hearing aid info
+     *
+     * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
+     * intent will not be sent.
+     */
+    public static @NonNull BtProfileConnectionInfo hearingAidInfo(boolean suppressNoisyIntent) {
+        return new BtProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, -1,
+            false);
+    }
+
+    /**
+     * constructor for le audio info
+     *
+     * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
+     * intent will not be sent.
+     *
+     * @param isLeOutput if true mean the device is an output device, if false it's an input device
+     */
+    public static @NonNull BtProfileConnectionInfo leAudio(boolean suppressNoisyIntent,
+            boolean isLeOutput) {
+        return new BtProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, -1,
+            isLeOutput);
+    }
+
+    /**
+     * @return The profile connection
+     */
+    public @BtProfile int getProfile() {
+        return mProfile;
+    }
+
+    /**
+     * @return {@code true} if {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be
+     * sent
+     */
+    public boolean getSuppressNoisyIntent() {
+        return mSupprNoisy;
+    }
+
+    /**
+     * Only for {@link BluetoothProfile.A2DP} profile
+     * @return the volume of the connection or -1 if the value is ignored
+     */
+    public int getVolume() {
+        return mVolume;
+    }
+
+    /**
+     * Only for {@link BluetoothProfile.LE_AUDIO} profile
+     * @return {@code true} is the LE device is an output device, {@code false} if it's an input
+     * device
+     */
+    public boolean getIsLeOutput() {
+        return mIsLeOutput;
+    }
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index dd44fdf..5ff56f9 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -24,6 +24,7 @@
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioRecordingConfiguration;
 import android.media.AudioRoutesInfo;
+import android.media.BtProfileConnectionInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioModeDispatcher;
 import android.media.IAudioRoutesObserver;
@@ -207,8 +208,6 @@
     void setWiredDeviceConnectionState(int type, int state, String address, String name,
             String caller);
 
-    void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device);
-
     @UnsupportedAppUsage
     AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
 
@@ -268,16 +267,8 @@
 
     oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
 
-    void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
-            int state, boolean suppressNoisyIntent, int musicDevice);
-
-    void setBluetoothLeAudioOutDeviceConnectionState(in BluetoothDevice device, int state,
-            boolean suppressNoisyIntent);
-
-    void setBluetoothLeAudioInDeviceConnectionState(in BluetoothDevice device, int state);
-
-    void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
-            int state, int profile, boolean suppressNoisyIntent, int a2dpVolume);
+    void handleBluetoothActiveDeviceChanged(in BluetoothDevice newDevice,
+            in BluetoothDevice previousDevice, in BtProfileConnectionInfo info);
 
     oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
             in IAudioPolicyCallback pcb);
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index ce23a8b..e1da744 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -11,6 +11,7 @@
 awickham@google.com
 beverlyt@google.com
 brockman@google.com
+brycelee@google.com
 ccassidy@google.com
 cinek@google.com
 cwren@google.com
@@ -71,4 +72,4 @@
 hseog@google.com
 
 #Android TV
-rgl@google.com
\ No newline at end of file
+rgl@google.com
diff --git a/services/core/Android.bp b/services/core/Android.bp
index ad3c844..2103bcc 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -151,7 +151,7 @@
         "android.hardware.biometrics.fingerprint-V2.3-java",
         "android.hardware.biometrics.fingerprint-V1-java",
         "android.hardware.oemlock-V1.0-java",
-        "android.hardware.configstore-V1.0-java",
+        "android.hardware.configstore-V1.1-java",
         "android.hardware.contexthub-V1.0-java",
         "android.hardware.rebootescrow-V1-java",
         "android.hardware.soundtrigger-V2.3-java",
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index c383f51..0b2311b 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -17,12 +17,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothLeAudio;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -32,6 +29,7 @@
 import android.media.AudioManager;
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
+import android.media.BtProfileConnectionInfo;
 import android.media.IAudioRoutesObserver;
 import android.media.ICapturePresetDevicesRoleDispatcher;
 import android.media.ICommunicationDeviceDispatcher;
@@ -516,29 +514,82 @@
         }
     };
 
-    /*package*/ static final class BtDeviceConnectionInfo {
-        final @NonNull BluetoothDevice mDevice;
-        final @AudioService.BtProfileConnectionState int mState;
-        final int mProfile;
-        final boolean mSupprNoisy;
-        final int mVolume;
+    /*package*/ static final class BtDeviceChangedData {
+        final @Nullable BluetoothDevice mNewDevice;
+        final @Nullable BluetoothDevice mPreviousDevice;
+        final @NonNull BtProfileConnectionInfo mInfo;
+        final @NonNull String mEventSource;
 
-        BtDeviceConnectionInfo(@NonNull BluetoothDevice device,
-                @AudioService.BtProfileConnectionState int state,
-                int profile, boolean suppressNoisyIntent, int vol) {
-            mDevice = device;
-            mState = state;
-            mProfile = profile;
-            mSupprNoisy = suppressNoisyIntent;
-            mVolume = vol;
+        BtDeviceChangedData(@Nullable BluetoothDevice newDevice,
+                @Nullable BluetoothDevice previousDevice,
+                @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) {
+            mNewDevice = newDevice;
+            mPreviousDevice = previousDevice;
+            mInfo = info;
+            mEventSource = eventSource;
         }
 
-        BtDeviceConnectionInfo(@NonNull BtDeviceConnectionInfo info) {
-            mDevice = info.mDevice;
-            mState = info.mState;
-            mProfile = info.mProfile;
-            mSupprNoisy = info.mSupprNoisy;
-            mVolume = info.mVolume;
+        @Override
+        public String toString() {
+            return "BtDeviceChangedData profile=" + BluetoothProfile.getProfileName(
+                    mInfo.getProfile())
+                + ", switch device: [" + mPreviousDevice + "] -> [" + mNewDevice + "]";
+        }
+    }
+
+    /*package*/ static final class BtDeviceInfo {
+        final @NonNull BluetoothDevice mDevice;
+        final @AudioService.BtProfileConnectionState int mState;
+        final @AudioService.BtProfile int mProfile;
+        final boolean mSupprNoisy;
+        final int mVolume;
+        final boolean mIsLeOutput;
+        final @NonNull String mEventSource;
+        final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec;
+        final int mAudioSystemDevice;
+        final int mMusicDevice;
+
+        BtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, int state,
+                    int audioDevice, @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) {
+            mDevice = device;
+            mState = state;
+            mProfile = d.mInfo.getProfile();
+            mSupprNoisy = d.mInfo.getSuppressNoisyIntent();
+            mVolume = d.mInfo.getVolume();
+            mIsLeOutput = d.mInfo.getIsLeOutput();
+            mEventSource = d.mEventSource;
+            mAudioSystemDevice = audioDevice;
+            mMusicDevice = AudioSystem.DEVICE_NONE;
+            mCodec = codec;
+        }
+
+        // constructor used by AudioDeviceBroker to search similar message
+        BtDeviceInfo(@NonNull BluetoothDevice device, int profile) {
+            mDevice = device;
+            mProfile = profile;
+            mEventSource = "";
+            mMusicDevice = AudioSystem.DEVICE_NONE;
+            mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
+            mAudioSystemDevice = 0;
+            mState = 0;
+            mSupprNoisy = false;
+            mVolume = -1;
+            mIsLeOutput = false;
+        }
+
+        // constructor used by AudioDeviceInventory when config change failed
+        BtDeviceInfo(@NonNull BluetoothDevice device, int profile, int state, int musicDevice,
+                    int audioSystemDevice) {
+            mDevice = device;
+            mProfile = profile;
+            mEventSource = "";
+            mMusicDevice = musicDevice;
+            mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
+            mAudioSystemDevice = audioSystemDevice;
+            mState = state;
+            mSupprNoisy = false;
+            mVolume = -1;
+            mIsLeOutput = false;
         }
 
         // redefine equality op so we can match messages intended for this device
@@ -550,16 +601,52 @@
             if (this == o) {
                 return true;
             }
-            if (o instanceof BtDeviceConnectionInfo) {
-                return mDevice.equals(((BtDeviceConnectionInfo) o).mDevice);
+            if (o instanceof BtDeviceInfo) {
+                return mProfile == ((BtDeviceInfo) o).mProfile
+                    && mDevice.equals(((BtDeviceInfo) o).mDevice);
             }
             return false;
         }
+    }
 
-        @Override
-        public String toString() {
-            return "BtDeviceConnectionInfo dev=" + mDevice.toString();
+    BtDeviceInfo createBtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device,
+                int state) {
+        int audioDevice;
+        int codec = AudioSystem.AUDIO_FORMAT_DEFAULT;
+        switch (d.mInfo.getProfile()) {
+            case BluetoothProfile.A2DP_SINK:
+                audioDevice = AudioSystem.DEVICE_IN_BLUETOOTH_A2DP;
+                break;
+            case BluetoothProfile.A2DP:
+                audioDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+                synchronized (mDeviceStateLock) {
+                    codec = mBtHelper.getA2dpCodec(device);
+                }
+                break;
+            case BluetoothProfile.HEARING_AID:
+                audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID;
+                break;
+            case BluetoothProfile.LE_AUDIO:
+                if (d.mInfo.getIsLeOutput()) {
+                    audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET;
+                } else {
+                    audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET;
+                }
+                break;
+            default: throw new IllegalArgumentException("Invalid profile " + d.mInfo.getProfile());
         }
+        return new BtDeviceInfo(d, device, state, audioDevice, codec);
+    }
+
+    private void btMediaMetricRecord(@NonNull BluetoothDevice device, String state,
+            @NonNull BtDeviceChangedData data) {
+        final String name = TextUtils.emptyIfNull(device.getName());
+        new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
+                + "queueOnBluetoothActiveDeviceChanged")
+            .set(MediaMetrics.Property.STATE, state)
+            .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
+            .set(MediaMetrics.Property.NAME, name)
+            .record();
     }
 
     /**
@@ -567,116 +654,37 @@
      * not just a simple message post
      * @param info struct with the (dis)connection information
      */
-    /*package*/ void queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-            @NonNull BtDeviceConnectionInfo info) {
-        final String name = TextUtils.emptyIfNull(info.mDevice.getName());
-        new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
-                + "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent")
-                .set(MediaMetrics.Property.STATE, info.mState == BluetoothProfile.STATE_CONNECTED
-                        ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
-                .set(MediaMetrics.Property.INDEX, info.mVolume)
-                .set(MediaMetrics.Property.NAME, name)
-                .record();
-
-        // operations of removing and posting messages related to A2DP device state change must be
-        // mutually exclusive
-        synchronized (mDeviceStateLock) {
-            // when receiving a request to change the connection state of a device, this last
-            // request is the source of truth, so cancel all previous requests that are already in
-            // the handler
-            removeScheduledA2dpEvents(info.mDevice);
-
-            sendLMsgNoDelay(
-                    info.mState == BluetoothProfile.STATE_CONNECTED
-                            ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
-                            : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
-                    SENDMSG_QUEUE, info);
+    /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
+        if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null
+                && data.mPreviousDevice.equals(data.mNewDevice)) {
+            final String name = TextUtils.emptyIfNull(data.mNewDevice.getName());
+            new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
+                    + "queueOnBluetoothActiveDeviceChanged_update")
+                    .set(MediaMetrics.Property.NAME, name)
+                    .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
+                    .record();
+            synchronized (mDeviceStateLock) {
+                postBluetoothA2dpDeviceConfigChange(data.mNewDevice);
+            }
+        } else {
+            synchronized (mDeviceStateLock) {
+                if (data.mPreviousDevice != null) {
+                    btMediaMetricRecord(data.mPreviousDevice, MediaMetrics.Value.DISCONNECTED,
+                            data);
+                    sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE,
+                            createBtDeviceInfo(data, data.mPreviousDevice,
+                                    BluetoothProfile.STATE_DISCONNECTED));
+                }
+                if (data.mNewDevice != null) {
+                    btMediaMetricRecord(data.mNewDevice, MediaMetrics.Value.CONNECTED, data);
+                    sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE,
+                            createBtDeviceInfo(data, data.mNewDevice,
+                                    BluetoothProfile.STATE_CONNECTED));
+                }
+            }
         }
     }
 
-    /** remove all previously scheduled connection and state change events for the given device */
-    @GuardedBy("mDeviceStateLock")
-    private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device) {
-        mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, device);
-
-        final BtDeviceConnectionInfo connectionInfoToRemove = new BtDeviceConnectionInfo(device,
-                // the next parameters of the constructor will be ignored when finding the message
-                // to remove as the equality of the message's object is tested on the device itself
-                // (see BtDeviceConnectionInfo.equals() method override)
-                BluetoothProfile.STATE_CONNECTED, 0, false, -1);
-        mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
-                connectionInfoToRemove);
-        mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
-                connectionInfoToRemove);
-
-        final BtHelper.BluetoothA2dpDeviceInfo devInfoToRemove =
-                new BtHelper.BluetoothA2dpDeviceInfo(device);
-        mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
-                devInfoToRemove);
-        mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
-                devInfoToRemove);
-        mBrokerHandler.removeEqualMessages(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE,
-                devInfoToRemove);
-    }
-
-    private static final class HearingAidDeviceConnectionInfo {
-        final @NonNull BluetoothDevice mDevice;
-        final @AudioService.BtProfileConnectionState int mState;
-        final boolean mSupprNoisy;
-        final int mMusicDevice;
-        final @NonNull String mEventSource;
-
-        HearingAidDeviceConnectionInfo(@NonNull BluetoothDevice device,
-                @AudioService.BtProfileConnectionState int state,
-                boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
-            mDevice = device;
-            mState = state;
-            mSupprNoisy = suppressNoisyIntent;
-            mMusicDevice = musicDevice;
-            mEventSource = eventSource;
-        }
-    }
-
-    /*package*/ void postBluetoothHearingAidDeviceConnectionState(
-            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
-            boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
-        final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
-                device, state, suppressNoisyIntent, musicDevice, eventSource);
-        sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
-    }
-
-    private static final class LeAudioDeviceConnectionInfo {
-        final @NonNull BluetoothDevice mDevice;
-        final @AudioService.BtProfileConnectionState int mState;
-        final boolean mSupprNoisy;
-        final @NonNull String mEventSource;
-
-        LeAudioDeviceConnectionInfo(@NonNull BluetoothDevice device,
-                @AudioService.BtProfileConnectionState int state,
-                boolean suppressNoisyIntent, @NonNull String eventSource) {
-            mDevice = device;
-            mState = state;
-            mSupprNoisy = suppressNoisyIntent;
-            mEventSource = eventSource;
-        }
-    }
-
-    /*package*/ void postBluetoothLeAudioOutDeviceConnectionState(
-            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
-            boolean suppressNoisyIntent, @NonNull String eventSource) {
-        final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo(
-                device, state, suppressNoisyIntent, eventSource);
-        sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
-    }
-
-    /*package*/ void postBluetoothLeAudioInDeviceConnectionState(
-            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
-            @NonNull String eventSource) {
-        final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo(
-                device, state, false, eventSource);
-        sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
-    }
-
     /**
      * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
      */
@@ -926,19 +934,8 @@
     }
 
     @GuardedBy("mDeviceStateLock")
-    /*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state,
-            @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
-        sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
-                        ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
-                        : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
-                SENDMSG_QUEUE,
-                state, btDeviceInfo, delay);
-    }
-
-    /*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state,
-            @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
-        sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
-                state, btDeviceInfo, delay);
+    /*package*/ void postBluetoothActiveDevice(BtDeviceInfo info, int delay) {
+        sendLMsg(MSG_L_SET_BT_ACTIVE_DEVICE, SENDMSG_QUEUE, info, delay);
     }
 
     /*package*/ void postSetWiredDeviceConnectionState(
@@ -946,72 +943,12 @@
         sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay);
     }
 
-    /*package*/ void postSetHearingAidConnectionState(
-            @AudioService.BtProfileConnectionState int state,
-            @NonNull BluetoothDevice device, int delay) {
-        sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
-                state,
-                device,
-                delay);
+    /*package*/ void postBtProfileDisconnected(int profile) {
+        sendIMsgNoDelay(MSG_I_BT_SERVICE_DISCONNECTED_PROFILE, SENDMSG_QUEUE, profile);
     }
 
-    /*package*/ void postSetLeAudioOutConnectionState(
-            @AudioService.BtProfileConnectionState int state,
-            @NonNull BluetoothDevice device, int delay) {
-        sendILMsg(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE, SENDMSG_QUEUE,
-                state,
-                device,
-                delay);
-    }
-
-    /*package*/ void postSetLeAudioInConnectionState(
-            @AudioService.BtProfileConnectionState int state,
-            @NonNull BluetoothDevice device) {
-        sendILMsgNoDelay(MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE, SENDMSG_QUEUE,
-                state,
-                device);
-    }
-
-    /*package*/ void postDisconnectA2dp() {
-        sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
-    }
-
-    /*package*/ void postDisconnectA2dpSink() {
-        sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
-    }
-
-    /*package*/ void postDisconnectHearingAid() {
-        sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
-    }
-
-    /*package*/ void postDisconnectLeAudio() {
-        sendMsgNoDelay(MSG_DISCONNECT_BT_LE_AUDIO, SENDMSG_QUEUE);
-    }
-
-    /*package*/ void postDisconnectHeadset() {
-        sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
-    }
-
-    /*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) {
-        sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
-    }
-
-    /*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) {
-        sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
-    }
-
-    /*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) {
-        sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile);
-    }
-
-    /*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) {
-        sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
-                hearingAidProfile);
-    }
-
-    /*package*/ void postBtLeAudioProfileConnected(BluetoothLeAudio leAudioProfile) {
-        sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO, SENDMSG_QUEUE,
-                leAudioProfile);
+    /*package*/ void postBtProfileConnected(int profile, BluetoothProfile proxy) {
+        sendILMsgNoDelay(MSG_IL_BT_SERVICE_CONNECTED_PROFILE, SENDMSG_QUEUE, profile, proxy);
     }
 
     /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
@@ -1069,13 +1006,6 @@
         }
     }
 
-    /*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
-            @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
-        final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
-        sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
-                btDeviceInfo);
-    }
-
     /*package*/ void handleFailureToConnectToBtHeadsetService(int delay) {
         sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
     }
@@ -1088,19 +1018,10 @@
         sendMsgNoDelay(fromA2dp ? MSG_REPORT_NEW_ROUTES_A2DP : MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
     }
 
-    /*package*/ void postA2dpActiveDeviceChange(
-                    @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
-        sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
-    }
-
     // must be called synchronized on mConnectedDevices
-    /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
-        final BtHelper.BluetoothA2dpDeviceInfo devInfoToCheck =
-                new BtHelper.BluetoothA2dpDeviceInfo(btDevice);
-        return (mBrokerHandler.hasEqualMessages(
-                    MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, devInfoToCheck)
-            || mBrokerHandler.hasEqualMessages(
-                    MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, devInfoToCheck));
+    /*package*/ boolean hasScheduledA2dpConnection(BluetoothDevice btDevice) {
+        final BtDeviceInfo devInfoToCheck = new BtDeviceInfo(btDevice, BluetoothProfile.A2DP);
+        return mBrokerHandler.hasEqualMessages(MSG_L_SET_BT_ACTIVE_DEVICE, devInfoToCheck);
     }
 
     /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) {
@@ -1124,12 +1045,6 @@
         }
     }
 
-    /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
-        synchronized (mDeviceStateLock) {
-            return mBtHelper.getA2dpCodec(device);
-        }
-    }
-
     /*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) {
         mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent);
     }
@@ -1285,39 +1200,11 @@
                         mDeviceInventory.onReportNewRoutes();
                     }
                     break;
-                case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
-                case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
+                case MSG_L_SET_BT_ACTIVE_DEVICE:
                     synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onSetA2dpSinkConnectionState(
-                                (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
-                    }
-                    break;
-                case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onSetA2dpSourceConnectionState(
-                                (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
-                    }
-                    break;
-                case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onSetHearingAidConnectionState(
-                                (BluetoothDevice) msg.obj, msg.arg1,
+                        mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj,
                                 mAudioService.getBluetoothContextualVolumeStream());
                     }
-                    break;
-                case MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onSetLeAudioOutConnectionState(
-                                (BluetoothDevice) msg.obj, msg.arg1,
-                                mAudioService.getBluetoothContextualVolumeStream());
-                    }
-                    break;
-                case MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onSetLeAudioInConnectionState(
-                                (BluetoothDevice) msg.obj, msg.arg1);
-                    }
-                    break;
                 case MSG_BT_HEADSET_CNCT_FAILED:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
@@ -1332,14 +1219,10 @@
                     }
                     break;
                 case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
-                    final int a2dpCodec;
                     final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
                     synchronized (mDeviceStateLock) {
-                        a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
-                        // TODO: name of method being called on AudioDeviceInventory is currently
-                        //       misleading (config change vs active device change), to be
-                        //       reconciliated once the BT side has been updated.
-                        mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
+                        final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
+                        mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
                                 new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
                                         BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
                     }
@@ -1392,96 +1275,47 @@
                         mDeviceInventory.onToggleHdmi();
                     }
                     break;
-                case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
-                                (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
-                                 BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
-                    }
-                    break;
-                case MSG_DISCONNECT_A2DP:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.disconnectA2dp();
-                    }
-                    break;
-                case MSG_DISCONNECT_A2DP_SINK:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.disconnectA2dpSink();
-                    }
-                    break;
-                case MSG_DISCONNECT_BT_HEARING_AID:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.disconnectHearingAid();
-                    }
-                    break;
-                case MSG_DISCONNECT_BT_HEADSET:
-                    synchronized (mSetModeLock) {
+                case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE:
+                    if (msg.arg1 != BluetoothProfile.HEADSET) {
                         synchronized (mDeviceStateLock) {
-                            mBtHelper.disconnectHeadset();
+                            mDeviceInventory.onBtProfileDisconnected(msg.arg1);
+                        }
+                    } else {
+                        synchronized (mSetModeLock) {
+                            synchronized (mDeviceStateLock) {
+                                mBtHelper.disconnectHeadset();
+                            }
                         }
                     }
                     break;
-                case MSG_DISCONNECT_BT_LE_AUDIO:
-                    synchronized(mDeviceStateLock) {
-                        mDeviceInventory.disconnectLeAudio();
-                    }
-                    break;
-                case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
-                    synchronized (mDeviceStateLock) {
-                        mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
-                    }
-                    break;
-                case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK:
-                    synchronized (mDeviceStateLock) {
-                        mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
-                    }
-                    break;
-                case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID:
-                    synchronized (mDeviceStateLock) {
-                        mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
-                    }
-                    break;
-
-                case MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO:
-                    synchronized(mDeviceStateLock) {
-                        mBtHelper.onLeAudioProfileConnected((BluetoothLeAudio) msg.obj);
-                    }
-                    break;
-                case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
-                    synchronized (mSetModeLock) {
+                case MSG_IL_BT_SERVICE_CONNECTED_PROFILE:
+                    if (msg.arg1 != BluetoothProfile.HEADSET) {
                         synchronized (mDeviceStateLock) {
-                            mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+                            mBtHelper.onBtProfileConnected(msg.arg1, (BluetoothProfile) msg.obj);
+                        }
+                    } else {
+                        synchronized (mSetModeLock) {
+                            synchronized (mDeviceStateLock) {
+                                mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+                            }
                         }
                     }
                     break;
-                case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
-                case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: {
-                    final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj;
+                case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: {
+                    final BtDeviceInfo info = (BtDeviceInfo) msg.obj;
+                    if (info.mDevice == null) break;
                     AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
-                            "msg: setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent "
+                            "msg: onBluetoothActiveDeviceChange "
                                     + " state=" + info.mState
                                     // only querying address as this is the only readily available
                                     // field on the device
                                     + " addr=" + info.mDevice.getAddress()
-                                    + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
-                                    + " vol=" + info.mVolume)).printLog(TAG));
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
-                                info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
-                                AudioSystem.DEVICE_NONE, info.mVolume);
-                    }
-                } break;
-                case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
-                    final HearingAidDeviceConnectionInfo info =
-                            (HearingAidDeviceConnectionInfo) msg.obj;
-                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
-                            "msg: setHearingAidDeviceConnectionState state=" + info.mState
-                                    + " addr=" + info.mDevice.getAddress()
+                                    + " prof=" + info.mProfile
                                     + " supprNoisy=" + info.mSupprNoisy
-                                    + " src=" + info.mEventSource)).printLog(TAG));
+                                    + " src=" + info.mEventSource
+                                    )).printLog(TAG));
                     synchronized (mDeviceStateLock) {
-                        mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
-                                info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
+                        mDeviceInventory.setBluetoothActiveDevice(info);
                     }
                 } break;
                 case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
@@ -1524,31 +1358,6 @@
                     final int capturePreset = msg.arg1;
                     mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
                 } break;
-                case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: {
-                    final LeAudioDeviceConnectionInfo info =
-                            (LeAudioDeviceConnectionInfo) msg.obj;
-                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
-                            "setLeAudioDeviceOutConnectionState state=" + info.mState
-                                    + " addr=" + info.mDevice.getAddress()
-                                    + " supprNoisy=" + info.mSupprNoisy
-                                    + " src=" + info.mEventSource)).printLog(TAG));
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.setBluetoothLeAudioOutDeviceConnectionState(
-                                info.mDevice, info.mState, info.mSupprNoisy);
-                    }
-                } break;
-                case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: {
-                    final LeAudioDeviceConnectionInfo info =
-                            (LeAudioDeviceConnectionInfo) msg.obj;
-                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
-                            "setLeAudioDeviceInConnectionState state=" + info.mState
-                                    + " addr=" + info.mDevice.getAddress()
-                                    + " src=" + info.mEventSource)).printLog(TAG));
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.setBluetoothLeAudioInDeviceConnectionState(info.mDevice,
-                                info.mState);
-                    }
-                } break;
                 default:
                     Log.wtf(TAG, "Invalid message " + msg.what);
             }
@@ -1579,8 +1388,7 @@
     private static final int MSG_IIL_SET_FORCE_USE = 4;
     private static final int MSG_IIL_SET_FORCE_BT_A2DP_USE = 5;
     private static final int MSG_TOGGLE_HDMI = 6;
-    private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7;
-    private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8;
+    private static final int MSG_L_SET_BT_ACTIVE_DEVICE = 7;
     private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
     private static final int MSG_IL_BTA2DP_TIMEOUT = 10;
 
@@ -1593,25 +1401,11 @@
     private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15;
     private static final int MSG_I_SET_MODE_OWNER_PID = 16;
 
-    // process active A2DP device change, obj is BtHelper.BluetoothA2dpDeviceInfo
-    private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18;
-
-    private static final int MSG_DISCONNECT_A2DP = 19;
-    private static final int MSG_DISCONNECT_A2DP_SINK = 20;
-    private static final int MSG_DISCONNECT_BT_HEARING_AID = 21;
-    private static final int MSG_DISCONNECT_BT_HEADSET = 22;
-    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP = 23;
-    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24;
-    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25;
-    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26;
-
-    // process change of state, obj is BtHelper.BluetoothA2dpDeviceInfo
-    private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED = 27;
-    private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED = 28;
+    private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22;
+    private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23;
 
     // process external command to (dis)connect an A2DP device, obj is BtDeviceConnectionInfo
-    private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION = 29;
-    private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION = 30;
+    private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT = 29;
 
     // process external command to (dis)connect a hearing aid device
     private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
@@ -1630,33 +1424,21 @@
     private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40;
     private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41;
 
-    private static final int MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE = 42;
-    private static final int MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE = 43;
-    private static final int MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT = 44;
-    private static final int MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT = 45;
+    private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45;
+    //
     // process set volume for Le Audio, obj is BleVolumeInfo
     private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46;
 
-    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO = 47;
-    private static final int MSG_DISCONNECT_BT_LE_AUDIO = 48;
-
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
             case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
-            case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
-            case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
-            case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
-            case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
+            case MSG_L_SET_BT_ACTIVE_DEVICE:
             case MSG_IL_BTA2DP_TIMEOUT:
             case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
             case MSG_TOGGLE_HDMI:
-            case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
-            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
-            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION:
+            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
             case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
             case MSG_CHECK_MUTE_MUSIC:
-            case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT:
-            case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT:
                 return true;
             default:
                 return false;
@@ -1739,14 +1521,10 @@
             long time = SystemClock.uptimeMillis() + delay;
 
             switch (msg) {
-                case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
-                case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
-                case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
-                case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
+                case MSG_L_SET_BT_ACTIVE_DEVICE:
                 case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
                 case MSG_IL_BTA2DP_TIMEOUT:
                 case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
-                case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
                     if (sLastDeviceConnectMsgTime >= time) {
                         // add a little delay to make sure messages are ordered as expected
                         time = sLastDeviceConnectMsgTime + 30;
@@ -1765,14 +1543,9 @@
     private static final Set<Integer> MESSAGES_MUTE_MUSIC;
     static {
         MESSAGES_MUTE_MUSIC = new HashSet<>();
-        MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED);
-        MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED);
-        MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE);
+        MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE);
         MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE);
-        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE);
-        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION);
-        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION);
-        MESSAGES_MUTE_MUSIC.add(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT);
+        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT);
         MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE);
         MESSAGES_MUTE_MUSIC.add(MSG_REPORT_NEW_ROUTES_A2DP);
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 6c3c736..0a114b9 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -16,11 +16,8 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
-import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.content.Intent;
 import android.media.AudioDeviceAttributes;
@@ -286,186 +283,102 @@
         }
     }
 
-    // only public for mocking/spying
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    @VisibleForTesting
-    public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo,
-            @AudioService.BtProfileConnectionState int state) {
-        final BluetoothDevice btDevice = btInfo.getBtDevice();
-        int a2dpVolume = btInfo.getVolume();
+    void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) {
         if (AudioService.DEBUG_DEVICES) {
-            Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state="
-                    + state + " vol=" + a2dpVolume);
+            Log.d(TAG, "onSetBtActiveDevice"
+                    + " btDevice=" + btInfo.mDevice
+                    + " profile=" + BluetoothProfile.getProfileName(btInfo.mProfile)
+                    + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState));
         }
-        String address = btDevice.getAddress();
-        if (address == null) {
-            address = "";
-        }
+        String address = btInfo.mDevice.getAddress();
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
             address = "";
         }
 
-        final @AudioSystem.AudioFormatNativeEnumForBtCodec int a2dpCodec = btInfo.getCodec();
+        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent("BT connected:"
+                        + " addr=" + address
+                        + " profile=" + btInfo.mProfile
+                        + " state=" + btInfo.mState
+                        + " codec=" + AudioSystem.audioFormatToString(btInfo.mCodec)));
 
-        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                "A2DP sink connected: device addr=" + address + " state=" + state
-                        + " codec=" + AudioSystem.audioFormatToString(a2dpCodec)
-                        + " vol=" + a2dpVolume));
-
-        new MediaMetrics.Item(mMetricsId + "a2dp")
+        new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice")
+                .set(MediaMetrics.Property.STATUS, btInfo.mProfile)
+                .set(MediaMetrics.Property.DEVICE,
+                        AudioSystem.getDeviceName(btInfo.mAudioSystemDevice))
                 .set(MediaMetrics.Property.ADDRESS, address)
-                .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec))
-                .set(MediaMetrics.Property.EVENT, "onSetA2dpSinkConnectionState")
-                .set(MediaMetrics.Property.INDEX, a2dpVolume)
+                .set(MediaMetrics.Property.ENCODING,
+                        AudioSystem.audioFormatToString(btInfo.mCodec))
+                .set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice")
+                .set(MediaMetrics.Property.STREAM_TYPE,
+                        AudioSystem.streamToString(streamType))
                 .set(MediaMetrics.Property.STATE,
-                        state == BluetoothProfile.STATE_CONNECTED
+                        btInfo.mState == BluetoothProfile.STATE_CONNECTED
                         ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
                 .record();
 
         synchronized (mDevicesLock) {
-            final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                    btDevice.getAddress());
+            final String key = DeviceInfo.makeDeviceListKey(btInfo.mAudioSystemDevice, address);
             final DeviceInfo di = mConnectedDevices.get(key);
-            boolean isConnected = di != null;
 
-            if (isConnected) {
-                if (state == BluetoothProfile.STATE_CONNECTED) {
-                    // device is already connected, but we are receiving a connection again,
-                    // it could be for a codec change
-                    if (a2dpCodec != di.mDeviceCodecFormat) {
-                        mDeviceBroker.postBluetoothA2dpDeviceConfigChange(btDevice);
+            final boolean isConnected = di != null;
+
+            final boolean switchToUnavailable = isConnected
+                    && btInfo.mState != BluetoothProfile.STATE_CONNECTED;
+            final boolean switchToAvailable = !isConnected
+                    && btInfo.mState == BluetoothProfile.STATE_CONNECTED;
+
+            switch (btInfo.mProfile) {
+                case BluetoothProfile.A2DP_SINK:
+                    if (switchToUnavailable) {
+                        makeA2dpSrcUnavailable(address);
+                    } else if (switchToAvailable) {
+                        makeA2dpSrcAvailable(address);
                     }
-                } else {
-                    makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
-                }
-            } else if (state == BluetoothProfile.STATE_CONNECTED) {
-                // device is not already connected
-                if (a2dpVolume != -1) {
-                    mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
-                            // convert index to internal representation in VolumeStreamState
-                            a2dpVolume * 10,
-                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
-                }
-                makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
-                        "onSetA2dpSinkConnectionState", a2dpCodec);
+                    break;
+                case BluetoothProfile.A2DP:
+                    if (switchToUnavailable) {
+                        makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
+                    } else if (switchToAvailable) {
+                        // device is not already connected
+                        if (btInfo.mVolume != -1) {
+                            mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+                                    // convert index to internal representation in VolumeStreamState
+                                    btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
+                                    "onSetBtActiveDevice");
+                        }
+                        makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
+                                "onSetBtActiveDevice", btInfo.mCodec);
+                    }
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    if (switchToUnavailable) {
+                        makeHearingAidDeviceUnavailable(address);
+                    } else if (switchToAvailable) {
+                        makeHearingAidDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
+                                streamType, "onSetBtActiveDevice");
+                    }
+                    break;
+                case BluetoothProfile.LE_AUDIO:
+                    if (switchToUnavailable) {
+                        makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice);
+                    } else if (switchToAvailable) {
+                        makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
+                                streamType, btInfo.mAudioSystemDevice, "onSetBtActiveDevice");
+                    }
+                    break;
+                default: throw new IllegalArgumentException("Invalid profile "
+                                 + BluetoothProfile.getProfileName(btInfo.mProfile));
             }
         }
     }
 
-    /*package*/ void onSetA2dpSourceConnectionState(
-            @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int state) {
-        final BluetoothDevice btDevice = btInfo.getBtDevice();
-        if (AudioService.DEBUG_DEVICES) {
-            Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state="
-                    + state);
-        }
-        String address = btDevice.getAddress();
-        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
-            address = "";
-        }
-
-        synchronized (mDevicesLock) {
-            final String key = DeviceInfo.makeDeviceListKey(
-                    AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
-            final DeviceInfo di = mConnectedDevices.get(key);
-            boolean isConnected = di != null;
-
-            new MediaMetrics.Item(mMetricsId + "onSetA2dpSourceConnectionState")
-                    .set(MediaMetrics.Property.ADDRESS, address)
-                    .set(MediaMetrics.Property.DEVICE,
-                            AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP))
-                    .set(MediaMetrics.Property.STATE,
-                            state == BluetoothProfile.STATE_CONNECTED
-                            ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
-                    .record();
-
-            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
-                makeA2dpSrcUnavailable(address);
-            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
-                makeA2dpSrcAvailable(address);
-            }
-        }
-    }
-
-    /*package*/ void onSetHearingAidConnectionState(BluetoothDevice btDevice,
-                @AudioService.BtProfileConnectionState int state, int streamType) {
-        String address = btDevice.getAddress();
-        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
-            address = "";
-        }
-        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                "onSetHearingAidConnectionState addr=" + address));
-
-        new MediaMetrics.Item(mMetricsId + "onSetHearingAidConnectionState")
-                .set(MediaMetrics.Property.ADDRESS, address)
-                .set(MediaMetrics.Property.DEVICE,
-                        AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP))
-                .set(MediaMetrics.Property.STATE,
-                        state == BluetoothProfile.STATE_CONNECTED
-                                ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
-                .set(MediaMetrics.Property.STREAM_TYPE,
-                        AudioSystem.streamToString(streamType))
-                .record();
-
-        synchronized (mDevicesLock) {
-            final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID,
-                    btDevice.getAddress());
-            final DeviceInfo di = mConnectedDevices.get(key);
-            boolean isConnected = di != null;
-
-            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
-                makeHearingAidDeviceUnavailable(address);
-            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
-                makeHearingAidDeviceAvailable(address, BtHelper.getName(btDevice), streamType,
-                        "onSetHearingAidConnectionState");
-            }
-        }
-    }
-
-    /*package*/ void onSetLeAudioConnectionState(BluetoothDevice btDevice,
-                @AudioService.BtProfileConnectionState int state, int streamType, int device) {
-        String address = btDevice.getAddress();
-        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
-            address = "";
-        }
-        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                "onSetLeAudioConnectionState addr=" + address));
-
-        synchronized (mDevicesLock) {
-            DeviceInfo di = null;
-            boolean isConnected = false;
-
-            String key = DeviceInfo.makeDeviceListKey(device, btDevice.getAddress());
-            di = mConnectedDevices.get(key);
-            isConnected = di != null;
-
-            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
-                makeLeAudioDeviceUnavailable(address, device);
-            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
-                makeLeAudioDeviceAvailable(address, BtHelper.getName(btDevice), streamType,
-                        device, "onSetLeAudioConnectionState");
-            }
-        }
-    }
-
-    /*package*/ void onSetLeAudioOutConnectionState(BluetoothDevice btDevice,
-                @AudioService.BtProfileConnectionState int state, int streamType) {
-        // TODO: b/198610537 clarify DEVICE_OUT_BLE_HEADSET vs DEVICE_OUT_BLE_SPEAKER criteria
-        onSetLeAudioConnectionState(btDevice, state, streamType,
-                AudioSystem.DEVICE_OUT_BLE_HEADSET);
-    }
-
-    /*package*/ void onSetLeAudioInConnectionState(BluetoothDevice btDevice,
-                @AudioService.BtProfileConnectionState int state) {
-        onSetLeAudioConnectionState(btDevice, state, AudioSystem.STREAM_DEFAULT,
-                AudioSystem.DEVICE_IN_BLE_HEADSET);
-    }
 
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-        /*package*/ void onBluetoothA2dpActiveDeviceChange(
+        /*package*/ void onBluetoothA2dpDeviceConfigChange(
             @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
         MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
-                + "onBluetoothA2dpActiveDeviceChange")
+                + "onBluetoothA2dpDeviceConfigChange")
                 .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event));
 
         final BluetoothDevice btDevice = btInfo.getBtDevice();
@@ -474,7 +387,7 @@
             return;
         }
         if (AudioService.DEBUG_DEVICES) {
-            Log.d(TAG, "onBluetoothA2dpActiveDeviceChange btDevice=" + btDevice);
+            Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
         }
         int a2dpVolume = btInfo.getVolume();
         @AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec();
@@ -484,11 +397,11 @@
             address = "";
         }
         AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                "onBluetoothA2dpActiveDeviceChange addr=" + address
+                "onBluetoothA2dpDeviceConfigChange addr=" + address
                     + " event=" + BtHelper.a2dpDeviceEventToString(event)));
 
         synchronized (mDevicesLock) {
-            if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) {
+            if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
                 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                         "A2dp config change ignored (scheduled connection change)")
                         .printLog(TAG));
@@ -500,7 +413,7 @@
                     AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
             final DeviceInfo di = mConnectedDevices.get(key);
             if (di == null) {
-                Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpActiveDeviceChange");
+                Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange");
                 mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record();
                 return;
             }
@@ -518,7 +431,7 @@
                             // convert index to internal representation in VolumeStreamState
                             a2dpVolume * 10,
                             AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                            "onBluetoothA2dpActiveDeviceChange");
+                            "onBluetoothA2dpDeviceConfigChange");
                 }
             } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
                 if (di.mDeviceCodecFormat != a2dpCodec) {
@@ -539,10 +452,9 @@
                 int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
                 // force A2DP device disconnection in case of error so that AudioService state is
                 // consistent with audio policy manager state
-                setBluetoothA2dpDeviceConnectionState(
-                        btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP,
-                        false /* suppressNoisyIntent */, musicDevice,
-                        -1 /* a2dpVolume */);
+                setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice,
+                                BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED,
+                                musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
             } else {
                 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                         "APM handleDeviceConfigChange success for A2DP device addr=" + address
@@ -828,7 +740,7 @@
     }
 
 
-    /*package*/ void disconnectA2dp() {
+    private void disconnectA2dp() {
         synchronized (mDevicesLock) {
             final ArraySet<String> toRemove = new ArraySet<>();
             // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
@@ -850,7 +762,7 @@
         }
     }
 
-    /*package*/ void disconnectA2dpSink() {
+    private void disconnectA2dpSink() {
         synchronized (mDevicesLock) {
             final ArraySet<String> toRemove = new ArraySet<>();
             // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices
@@ -865,7 +777,7 @@
         }
     }
 
-    /*package*/ void disconnectHearingAid() {
+    private void disconnectHearingAid() {
         synchronized (mDevicesLock) {
             final ArraySet<String> toRemove = new ArraySet<>();
             // Disconnect ALL DEVICE_OUT_HEARING_AID devices
@@ -887,6 +799,28 @@
         }
     }
 
+    /*package*/ synchronized void onBtProfileDisconnected(int profile) {
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                disconnectA2dp();
+                break;
+            case BluetoothProfile.A2DP_SINK:
+                disconnectA2dpSink();
+                break;
+            case BluetoothProfile.HEARING_AID:
+                disconnectHearingAid();
+                break;
+            case BluetoothProfile.LE_AUDIO:
+                disconnectLeAudio();
+                break;
+            default:
+                // Not a valid profile to disconnect
+                Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+        }
+    }
+
      /*package*/ void disconnectLeAudio() {
         synchronized (mDevicesLock) {
             final ArraySet<String> toRemove = new ArraySet<>();
@@ -934,46 +868,39 @@
     // only public for mocking/spying
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     @VisibleForTesting
-    public void setBluetoothA2dpDeviceConnectionState(
-            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
-            int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) {
+    public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) {
         int delay;
-        if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
-            throw new IllegalArgumentException("invalid profile " + profile);
-        }
         synchronized (mDevicesLock) {
-            if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
+            if (!info.mSupprNoisy
+                    && ((info.mProfile == BluetoothProfile.LE_AUDIO && info.mIsLeOutput)
+                        || info.mProfile == BluetoothProfile.HEARING_AID
+                        || info.mProfile == BluetoothProfile.A2DP)) {
                 @AudioService.ConnectionState int asState =
-                        (state == BluetoothA2dp.STATE_CONNECTED)
+                        (info.mState == BluetoothProfile.STATE_CONNECTED)
                                 ? AudioService.CONNECTION_STATE_CONNECTED
                                 : AudioService.CONNECTION_STATE_DISCONNECTED;
-                delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                        asState, musicDevice);
+                delay = checkSendBecomingNoisyIntentInt(info.mAudioSystemDevice, asState,
+                        info.mMusicDevice);
             } else {
                 delay = 0;
             }
 
-            final int a2dpCodec = mDeviceBroker.getA2dpCodec(device);
-
             if (AudioService.DEBUG_DEVICES) {
-                Log.i(TAG, "setBluetoothA2dpDeviceConnectionState device: " + device
-                        + " state: " + state + " delay(ms): " + delay
-                        + " codec:" + Integer.toHexString(a2dpCodec)
-                        + " suppressNoisyIntent: " + suppressNoisyIntent);
+                Log.i(TAG, "setBluetoothActiveDevice device: " + info.mDevice
+                        + " profile: " + BluetoothProfile.getProfileName(info.mProfile)
+                        + " state: " + BluetoothProfile.getConnectionStateName(info.mState)
+                        + " delay(ms): " + delay
+                        + " codec:" + Integer.toHexString(info.mCodec)
+                        + " suppressNoisyIntent: " + info.mSupprNoisy);
             }
-
-            final BtHelper.BluetoothA2dpDeviceInfo a2dpDeviceInfo =
-                    new BtHelper.BluetoothA2dpDeviceInfo(device, a2dpVolume, a2dpCodec);
-            if (profile == BluetoothProfile.A2DP) {
-                mDeviceBroker.postA2dpSinkConnection(state,
-                            a2dpDeviceInfo,
-                            delay);
-            } else { //profile == BluetoothProfile.A2DP_SINK
-                mDeviceBroker.postA2dpSourceConnection(state,
-                        a2dpDeviceInfo,
-                        delay);
+            mDeviceBroker.postBluetoothActiveDevice(info, delay);
+            if (info.mProfile == BluetoothProfile.HEARING_AID
+                    && info.mState == BluetoothProfile.STATE_CONNECTED) {
+                mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
+                                "HEARING_AID set to CONNECTED");
             }
         }
+        return delay;
     }
 
     /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
@@ -987,50 +914,6 @@
         }
     }
 
-    /*package*/ int  setBluetoothHearingAidDeviceConnectionState(
-            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
-            boolean suppressNoisyIntent, int musicDevice) {
-        int delay;
-        synchronized (mDevicesLock) {
-            if (!suppressNoisyIntent) {
-                int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
-                delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID,
-                        intState, musicDevice);
-            } else {
-                delay = 0;
-            }
-            mDeviceBroker.postSetHearingAidConnectionState(state, device, delay);
-            if (state == BluetoothHearingAid.STATE_CONNECTED) {
-                mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
-                                "HEARING_AID set to CONNECTED");
-            }
-            return delay;
-        }
-    }
-
-    /*package*/ int setBluetoothLeAudioOutDeviceConnectionState(
-            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
-            boolean suppressNoisyIntent) {
-        synchronized (mDevicesLock) {
-            /* Active device become null and it's previous device is not connected anymore */
-            int delay = 0;
-            if (!suppressNoisyIntent) {
-                int intState = (state == BluetoothLeAudio.STATE_CONNECTED) ? 1 : 0;
-                delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLE_HEADSET,
-                        intState, AudioSystem.DEVICE_NONE);
-            }
-            mDeviceBroker.postSetLeAudioOutConnectionState(state, device, delay);
-            return delay;
-        }
-    }
-
-    /*package*/ void setBluetoothLeAudioInDeviceConnectionState(
-            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state) {
-        synchronized (mDevicesLock) {
-            mDeviceBroker.postSetLeAudioInConnectionState(state, device);
-        }
-    }
-
     //-------------------------------------------------------------------
     // Internal utilities
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d75f21c..51784bb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -83,6 +83,7 @@
 import android.media.AudioRecordingConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
+import android.media.BtProfileConnectionInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioModeDispatcher;
 import android.media.IAudioRoutesObserver;
@@ -309,8 +310,8 @@
     private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
     private static final int MSG_UPDATE_AUDIO_MODE = 36;
     private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
-    private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38;
-    private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39;
+    private static final int MSG_BT_DEV_CHANGED = 38;
+
     private static final int MSG_DISPATCH_AUDIO_MODE = 40;
 
     // start of messages handled under wakelock
@@ -6266,77 +6267,44 @@
     public @interface BtProfileConnectionState {}
 
     /**
-     * See AudioManager.setBluetoothHearingAidDeviceConnectionState()
+     * @hide
+     * The profiles that can be used with AudioService.handleBluetoothActiveDeviceChanged()
      */
-    public void setBluetoothHearingAidDeviceConnectionState(
-            @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
-            boolean suppressNoisyIntent, int musicDevice)
-    {
-        if (device == null) {
-            throw new IllegalArgumentException("Illegal null device");
-        }
-        if (state != BluetoothProfile.STATE_CONNECTED
-                && state != BluetoothProfile.STATE_DISCONNECTED) {
-            throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
-                    + " (dis)connection, got " + state);
-        }
-        mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
-                device, state, suppressNoisyIntent, musicDevice, "AudioService");
-    }
+    @IntDef({
+            BluetoothProfile.HEARING_AID,
+            BluetoothProfile.A2DP,
+            BluetoothProfile.A2DP_SINK,
+            BluetoothProfile.LE_AUDIO,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BtProfile {}
 
-    private void setBluetoothLeAudioDeviceConnectionState(@NonNull BluetoothDevice device,
-            @BtProfileConnectionState int state) {
-        if (device == null) {
-            throw new IllegalArgumentException("Illegal null device");
-        }
-        if (state != BluetoothProfile.STATE_CONNECTED
-                && state != BluetoothProfile.STATE_DISCONNECTED) {
-            throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
-                    + " (dis)connection, got " + state);
-        }
-    }
 
     /**
-     * See AudioManager.setBluetoothLeAudioOutDeviceConnectionState()
+     * See AudioManager.handleBluetoothActiveDeviceChanged(...)
      */
-    public void setBluetoothLeAudioOutDeviceConnectionState(
-            @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
-            boolean suppressNoisyIntent) {
-        setBluetoothLeAudioDeviceConnectionState(device, state);
-        mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState(device, state,
-                suppressNoisyIntent, "AudioService");
-    }
-
-    /**
-     * See AudioManager.setBluetoothLeAudioInDeviceConnectionState()
-     */
-    public void setBluetoothLeAudioInDeviceConnectionState(
-            @NonNull BluetoothDevice device, @BtProfileConnectionState int state) {
-        setBluetoothLeAudioDeviceConnectionState(device, state);
-        mDeviceBroker.postBluetoothLeAudioInDeviceConnectionState(device, state, "AudioService");
-    }
-
-    /**
-     * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
-     */
-    public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-            @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
-            int profile, boolean suppressNoisyIntent, int a2dpVolume) {
-        if (device == null) {
-            throw new IllegalArgumentException("Illegal null device");
+    public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice,
+            BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Bluetooth is the only caller allowed");
         }
-        if (state != BluetoothProfile.STATE_CONNECTED
-                && state != BluetoothProfile.STATE_DISCONNECTED) {
-            throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
-                    + " (dis)connection, got " + state);
+        if (info == null) {
+            throw new IllegalArgumentException("Illegal null BtProfileConnectionInfo for device "
+                    + previousDevice + " -> " + newDevice);
         }
-
-        AudioDeviceBroker.BtDeviceConnectionInfo info =
-                new AudioDeviceBroker.BtDeviceConnectionInfo(device, state,
-                        profile, suppressNoisyIntent, a2dpVolume);
-        sendMsg(mAudioHandler, MSG_SET_A2DP_DEV_CONNECTION_STATE, SENDMSG_QUEUE,
-                0 /*arg1*/, 0 /*arg2*/,
-                /*obj*/ info, 0 /*delay*/);
+        final int profile = info.getProfile();
+        if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK
+                && profile != BluetoothProfile.LE_AUDIO
+                && profile != BluetoothProfile.HEARING_AID) {
+            throw new IllegalArgumentException("Illegal BluetoothProfile profile for device "
+                    + previousDevice + " -> " + newDevice + ". Got: " + profile);
+        }
+        AudioDeviceBroker.BtDeviceChangedData data =
+                new AudioDeviceBroker.BtDeviceChangedData(newDevice, previousDevice, info,
+                        "AudioService");
+        sendMsg(mAudioHandler, MSG_BT_DEV_CHANGED, SENDMSG_QUEUE, 0, 0,
+                /*obj*/ data, /*delay*/ 0);
     }
 
     /** only public for mocking/spying, do not call outside of AudioService */
@@ -6345,19 +6313,6 @@
         mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute);
     }
 
-    /**
-     * See AudioManager.handleBluetoothA2dpDeviceConfigChange()
-     * @param device
-     */
-    public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device)
-    {
-        if (device == null) {
-            throw new IllegalArgumentException("Illegal null device");
-        }
-        sendMsg(mAudioHandler, MSG_A2DP_DEV_CONFIG_CHANGE, SENDMSG_QUEUE, 0, 0,
-                /*obj*/ device, /*delay*/ 0);
-    }
-
     private static final Set<Integer> DEVICE_MEDIA_UNMUTED_ON_PLUG_SET;
     static {
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET = new HashSet<>();
@@ -7709,13 +7664,9 @@
                     }
                     break;
 
-                case MSG_SET_A2DP_DEV_CONNECTION_STATE:
-                    mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-                            (AudioDeviceBroker.BtDeviceConnectionInfo) msg.obj);
-                    break;
-
-                case MSG_A2DP_DEV_CONFIG_CHANGE:
-                    mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj);
+                case MSG_BT_DEV_CHANGED:
+                    mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+                            (AudioDeviceBroker.BtDeviceChangedData) msg.obj);
                     break;
 
                 case MSG_DISPATCH_AUDIO_MODE:
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index c924fde..9273a5d 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -31,6 +31,7 @@
 import android.media.AudioDeviceAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
+import android.media.BtProfileConnectionInfo;
 import android.os.Binder;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -451,11 +452,11 @@
     }
 
     /*package*/ synchronized void disconnectAllBluetoothProfiles() {
-        mDeviceBroker.postDisconnectA2dp();
-        mDeviceBroker.postDisconnectA2dpSink();
-        mDeviceBroker.postDisconnectHeadset();
-        mDeviceBroker.postDisconnectHearingAid();
-        mDeviceBroker.postDisconnectLeAudio();
+        mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP);
+        mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP_SINK);
+        mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEADSET);
+        mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEARING_AID);
+        mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.LE_AUDIO);
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
@@ -474,63 +475,32 @@
         mBluetoothHeadset = null;
     }
 
-    /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) {
-        mA2dp = a2dp;
-        final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices();
-        if (deviceList.isEmpty()) {
+    /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
+        if (profile == BluetoothProfile.HEADSET) {
+            onHeadsetProfileConnected((BluetoothHeadset) proxy);
             return;
         }
-        final BluetoothDevice btDevice = deviceList.get(0);
-        // the device is guaranteed CONNECTED
-        mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-                new AudioDeviceBroker.BtDeviceConnectionInfo(btDevice,
-                    BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK,
-                        true, -1));
-    }
-
-    /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) {
-        final List<BluetoothDevice> deviceList = profile.getConnectedDevices();
+        if (profile == BluetoothProfile.A2DP) {
+            mA2dp = (BluetoothA2dp) proxy;
+        } else if (profile == BluetoothProfile.LE_AUDIO) {
+            mLeAudio = (BluetoothLeAudio) proxy;
+        }
+        final List<BluetoothDevice> deviceList = proxy.getConnectedDevices();
         if (deviceList.isEmpty()) {
             return;
         }
         final BluetoothDevice btDevice = deviceList.get(0);
         final @BluetoothProfile.BtProfileState int state =
-                profile.getConnectionState(btDevice);
-        mDeviceBroker.postSetA2dpSourceConnectionState(
-                state, new BluetoothA2dpDeviceInfo(btDevice));
-    }
-
-    /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) {
-        mHearingAid = hearingAid;
-        final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices();
-        if (deviceList.isEmpty()) {
-            return;
+                proxy.getConnectionState(btDevice);
+        if (state == BluetoothProfile.STATE_CONNECTED) {
+            mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+                    new AudioDeviceBroker.BtDeviceChangedData(btDevice, null,
+                        new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener"));
+        } else {
+            mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+                    new AudioDeviceBroker.BtDeviceChangedData(null, btDevice,
+                        new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener"));
         }
-        final BluetoothDevice btDevice = deviceList.get(0);
-        final @BluetoothProfile.BtProfileState int state =
-                mHearingAid.getConnectionState(btDevice);
-        mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
-                btDevice, state,
-                /*suppressNoisyIntent*/ false,
-                /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
-                /*eventSource*/ "mBluetoothProfileServiceListener");
-    }
-
-    /*package*/ synchronized void onLeAudioProfileConnected(BluetoothLeAudio leAudio) {
-        mLeAudio = leAudio;
-        final List<BluetoothDevice> deviceList = mLeAudio.getConnectedDevices();
-        if (deviceList.isEmpty()) {
-            return;
-        }
-
-        final BluetoothDevice btDevice = deviceList.get(0);
-        final @BluetoothProfile.BtProfileState int state =
-        mLeAudio.getConnectionState(btDevice);
-        mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState(
-                btDevice, state,
-                /*suppressNoisyIntent*/ false,
-                /*musicDevice android.media.AudioSystem.DEVICE_NONE,*/
-                /*eventSource*/ "mBluetoothProfileServiceListener");
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
@@ -677,36 +647,16 @@
                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
                     switch(profile) {
                         case BluetoothProfile.A2DP:
-                            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                                    "BT profile service: connecting A2DP profile"));
-                            mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy);
-                            break;
-
                         case BluetoothProfile.A2DP_SINK:
-                            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                                    "BT profile service: connecting A2DP_SINK profile"));
-                            mDeviceBroker.postBtA2dpSinkProfileConnected(proxy);
-                            break;
-
                         case BluetoothProfile.HEADSET:
-                            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                                    "BT profile service: connecting HEADSET profile"));
-                            mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy);
-                            break;
-
                         case BluetoothProfile.HEARING_AID:
-                            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                                    "BT profile service: connecting HEARING_AID profile"));
-                            mDeviceBroker.postBtHearingAidProfileConnected(
-                                    (BluetoothHearingAid) proxy);
-                            break;
-
                         case BluetoothProfile.LE_AUDIO:
                             AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                                    "BT profile service: connecting LE_AUDIO profile"));
-                            mDeviceBroker.postBtLeAudioProfileConnected(
-                                    (BluetoothLeAudio) proxy);
+                                    "BT profile service: connecting "
+                                    + BluetoothProfile.getProfileName(profile) + " profile"));
+                            mDeviceBroker.postBtProfileConnected(profile, proxy);
                             break;
+
                         default:
                             break;
                     }
@@ -715,22 +665,11 @@
 
                     switch (profile) {
                         case BluetoothProfile.A2DP:
-                            mDeviceBroker.postDisconnectA2dp();
-                            break;
-
                         case BluetoothProfile.A2DP_SINK:
-                            mDeviceBroker.postDisconnectA2dpSink();
-                            break;
-
                         case BluetoothProfile.HEADSET:
-                            mDeviceBroker.postDisconnectHeadset();
-                            break;
-
                         case BluetoothProfile.HEARING_AID:
-                            mDeviceBroker.postDisconnectHearingAid();
-                            break;
                         case BluetoothProfile.LE_AUDIO:
-                            mDeviceBroker.postDisconnectLeAudio();
+                            mDeviceBroker.postBtProfileDisconnected(profile);
                             break;
 
                         default:
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 0cd2e3d..c97ad55 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -60,6 +60,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
@@ -70,6 +71,7 @@
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -81,6 +83,8 @@
     private static final String SETTING_HIDL_DISABLED =
             "com.android.server.biometrics.AuthService.hidlDisabled";
     private static final int DEFAULT_HIDL_DISABLED = 0;
+    private static final String SYSPROP_FIRST_API_LEVEL = "ro.board.first_api_level";
+    private static final String SYSPROP_API_LEVEL = "ro.board.api_level";
 
     private final Injector mInjector;
 
@@ -623,7 +627,16 @@
 
         final SensorConfig[] hidlConfigs;
         if (!mInjector.isHidlDisabled(getContext())) {
-            final String[] configStrings = mInjector.getConfiguration(getContext());
+            final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0);
+            final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel);
+            String[] configStrings = mInjector.getConfiguration(getContext());
+            if (configStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) {
+                // For backwards compatibility with R where biometrics could work without being
+                // configured in config_biometric_sensors. In the absence of a vendor provided
+                // configuration, we assume the weakest biometric strength (i.e. convenience).
+                Slog.w(TAG, "Found R vendor partition without config_biometric_sensors");
+                configStrings = generateRSdkCompatibleConfiguration();
+            }
             hidlConfigs = new SensorConfig[configStrings.length];
             for (int i = 0; i < configStrings.length; ++i) {
                 hidlConfigs[i] = new SensorConfig(configStrings[i]);
@@ -639,6 +652,31 @@
     }
 
     /**
+     * Generates an array of string configs with entries that correspond to the biometric features
+     * declared on the device. Returns an empty array if no biometric features are declared.
+     * Biometrics are assumed to be of the weakest strength class, i.e. convenience.
+     */
+    private @NonNull String[] generateRSdkCompatibleConfiguration() {
+        final PackageManager pm = getContext().getPackageManager();
+        final ArrayList<String> modalities = new ArrayList<>();
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FINGERPRINT));
+        }
+        if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FACE));
+        }
+        final String strength = String.valueOf(Authenticators.BIOMETRIC_CONVENIENCE);
+        final String[] configStrings = new String[modalities.size()];
+        for (int i = 0; i < modalities.size(); ++i) {
+            final String id = String.valueOf(i);
+            final String modality = modalities.get(i);
+            configStrings[i] = String.join(":" /* delimiter */, id, modality, strength);
+        }
+        Slog.d(TAG, "Generated config_biometric_sensors: " + Arrays.toString(configStrings));
+        return configStrings;
+    }
+
+    /**
      * Registers HIDL and AIDL authenticators for all of the available modalities.
      *
      * @param hidlSensors Array of {@link SensorConfig} configuration for all of the HIDL sensors
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 73de0f8..ffc1aed 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -51,6 +51,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 class BluetoothRouteProvider {
     private static final String TAG = "BTRouteProvider";
@@ -174,8 +175,9 @@
 
     private void buildBluetoothRoutes() {
         mBluetoothRoutes.clear();
-        if (mBluetoothAdapter.getBondedDevices() != null) {
-            for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
+        Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
+        if (bondedDevices != null) {
+            for (BluetoothDevice device : bondedDevices) {
                 if (device.isConnected()) {
                     BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
                     if (newBtRoute.connectedProfiles.size() > 0) {
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 28ae6a4..a15fc3e 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,11 +1,7 @@
 set noparent
 
-codewiz@google.com
-jchalard@google.com
+include platform/packages/modules/Connectivity:/OWNERS
+
 jsharkey@android.com
-junyulai@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
 sudheersai@google.com
 yamasani@google.com
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 827dfc0..1a0a885 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -21918,7 +21918,7 @@
         for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
             ScanPartition sp = SYSTEM_PARTITIONS.get(i);
             if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
-                    sp.getFolder().getAbsolutePath())) {
+                    sp.getFolder().getAbsolutePath() + File.separator)) {
                 return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
             }
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e408822..4a97720 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2666,6 +2666,24 @@
                 Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in"
                         + " interceptKeyBeforeQueueing");
                 return key_consumed;
+            case KeyEvent.KEYCODE_VIDEO_APP_1:
+            case KeyEvent.KEYCODE_VIDEO_APP_2:
+            case KeyEvent.KEYCODE_VIDEO_APP_3:
+            case KeyEvent.KEYCODE_VIDEO_APP_4:
+            case KeyEvent.KEYCODE_VIDEO_APP_5:
+            case KeyEvent.KEYCODE_VIDEO_APP_6:
+            case KeyEvent.KEYCODE_VIDEO_APP_7:
+            case KeyEvent.KEYCODE_VIDEO_APP_8:
+            case KeyEvent.KEYCODE_FEATURED_APP_1:
+            case KeyEvent.KEYCODE_FEATURED_APP_2:
+            case KeyEvent.KEYCODE_FEATURED_APP_3:
+            case KeyEvent.KEYCODE_FEATURED_APP_4:
+            case KeyEvent.KEYCODE_DEMO_APP_1:
+            case KeyEvent.KEYCODE_DEMO_APP_2:
+            case KeyEvent.KEYCODE_DEMO_APP_3:
+            case KeyEvent.KEYCODE_DEMO_APP_4:
+                Slog.wtf(TAG, "KEYCODE_APP_X should be handled in interceptKeyBeforeQueueing");
+                return key_consumed;
             case KeyEvent.KEYCODE_SYSRQ:
                 if (down && repeatCount == 0) {
                     mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
@@ -3773,6 +3791,26 @@
                 }
                 break;
             }
+            case KeyEvent.KEYCODE_VIDEO_APP_1:
+            case KeyEvent.KEYCODE_VIDEO_APP_2:
+            case KeyEvent.KEYCODE_VIDEO_APP_3:
+            case KeyEvent.KEYCODE_VIDEO_APP_4:
+            case KeyEvent.KEYCODE_VIDEO_APP_5:
+            case KeyEvent.KEYCODE_VIDEO_APP_6:
+            case KeyEvent.KEYCODE_VIDEO_APP_7:
+            case KeyEvent.KEYCODE_VIDEO_APP_8:
+            case KeyEvent.KEYCODE_FEATURED_APP_1:
+            case KeyEvent.KEYCODE_FEATURED_APP_2:
+            case KeyEvent.KEYCODE_FEATURED_APP_3:
+            case KeyEvent.KEYCODE_FEATURED_APP_4:
+            case KeyEvent.KEYCODE_DEMO_APP_1:
+            case KeyEvent.KEYCODE_DEMO_APP_2:
+            case KeyEvent.KEYCODE_DEMO_APP_3:
+            case KeyEvent.KEYCODE_DEMO_APP_4: {
+                // Just drop if keys are not intercepted for direct key.
+                result &= ~ACTION_PASS_TO_USER;
+                break;
+            }
         }
 
         // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 2894708..b9ceec1 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -315,6 +315,7 @@
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
                 userId);
         List<TvInputInfo> inputList = new ArrayList<>();
+        List<ComponentName> hardwareComponents = new ArrayList<>();
         for (ResolveInfo ri : services) {
             ServiceInfo si = ri.serviceInfo;
             if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
@@ -325,6 +326,7 @@
 
             ComponentName component = new ComponentName(si.packageName, si.name);
             if (hasHardwarePermission(pm, component)) {
+                hardwareComponents.add(component);
                 ServiceState serviceState = userState.serviceStateMap.get(component);
                 if (serviceState == null) {
                     // New hardware input found. Create a new ServiceState and connect to the
@@ -397,6 +399,15 @@
             }
         }
 
+        // Clean up ServiceState corresponding to the removed hardware inputs
+        Iterator<ServiceState> it = userState.serviceStateMap.values().iterator();
+        while (it.hasNext()) {
+            ServiceState serviceState = it.next();
+            if (serviceState.isHardware && !hardwareComponents.contains(serviceState.component)) {
+                it.remove();
+            }
+        }
+
         userState.inputMap.clear();
         userState.inputMap = inputMap;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9caef70..3421b28 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -167,8 +167,10 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
-import android.hardware.configstore.V1_0.ISurfaceFlingerConfigs;
 import android.hardware.configstore.V1_0.OptionalBool;
+import android.hardware.configstore.V1_1.DisplayOrientation;
+import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
+import android.hardware.configstore.V1_1.OptionalDisplayOrientation;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManager;
@@ -220,6 +222,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 import android.view.Display;
+import android.view.DisplayAddress;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -465,6 +468,8 @@
      */
     static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
             SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);
+    private @Surface.Rotation int mPrimaryDisplayOrientation = Surface.ROTATION_0;
+    private DisplayAddress mPrimaryDisplayPhysicalAddress;
 
     // Enums for animation scale update types.
     @Retention(RetentionPolicy.SOURCE)
@@ -2461,16 +2466,21 @@
             configChanged = displayContent.updateOrientation();
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
-            final DisplayInfo rotatedDisplayInfo =
-                    win.mToken.getFixedRotationTransformDisplayInfo();
-            if (rotatedDisplayInfo != null) {
-                outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation);
-            } else {
-                // We have to update the transform hint of display here, but we need to get if from
-                // SurfaceFlinger, so set it as rotation of display for most cases, then
-                // SurfaceFlinger would still update the transform hint of display in next frame.
-                outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation);
+            final DisplayInfo displayInfo = win.getDisplayInfo();
+            int transformHint = displayInfo.rotation;
+            // If the window is on the primary display, use the panel orientation to adjust the
+            // transform hint
+            final boolean isPrimaryDisplay = displayInfo.address != null &&
+                    displayInfo.address.equals(mPrimaryDisplayPhysicalAddress);
+            if (isPrimaryDisplay) {
+                transformHint = (transformHint + mPrimaryDisplayOrientation) % 4;
             }
+            outSurfaceControl.setTransformHint(transformHint);
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "Passing transform hint %d for window %s%s",
+                    transformHint, win,
+                    isPrimaryDisplay ? " on primary display with orientation "
+                            + mPrimaryDisplayOrientation : "");
 
             if (toBeDisplayed && win.mIsWallpaper) {
                 displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
@@ -4868,6 +4878,9 @@
         mTaskSnapshotController.systemReady();
         mHasWideColorGamutSupport = queryWideColorGamutSupport();
         mHasHdrSupport = queryHdrSupport();
+        mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation();
+        mPrimaryDisplayPhysicalAddress =
+            DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId());
         UiThread.getHandler().post(mSettingsObserver::loadSettings);
         IVrManager vrManager = IVrManager.Stub.asInterface(
                 ServiceManager.getService(Context.VR_SERVICE));
@@ -4887,6 +4900,9 @@
         }
     }
 
+
+    // Keep logic in sync with SurfaceFlingerProperties.cpp
+    // Consider exposing properties via ISurfaceComposer instead.
     private static boolean queryWideColorGamutSupport() {
         boolean defaultValue = false;
         Optional<Boolean> hasWideColorProp = SurfaceFlingerProperties.has_wide_color_display();
@@ -4927,6 +4943,39 @@
         return false;
     }
 
+    private static @Surface.Rotation int queryPrimaryDisplayOrientation() {
+        Optional<SurfaceFlingerProperties.primary_display_orientation_values> prop =
+                SurfaceFlingerProperties.primary_display_orientation();
+        if (prop.isPresent()) {
+            switch (prop.get()) {
+                case ORIENTATION_90: return Surface.ROTATION_90;
+                case ORIENTATION_180: return Surface.ROTATION_180;
+                case ORIENTATION_270: return Surface.ROTATION_270;
+                case ORIENTATION_0:
+                default:
+                    return Surface.ROTATION_0;
+            }
+        }
+        try {
+            ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
+            OptionalDisplayOrientation primaryDisplayOrientation =
+                    surfaceFlinger.primaryDisplayOrientation();
+            if (primaryDisplayOrientation != null && primaryDisplayOrientation.specified) {
+                switch (primaryDisplayOrientation.value) {
+                    case DisplayOrientation.ORIENTATION_90: return Surface.ROTATION_90;
+                    case DisplayOrientation.ORIENTATION_180: return Surface.ROTATION_180;
+                    case DisplayOrientation.ORIENTATION_270: return Surface.ROTATION_270;
+                    case DisplayOrientation.ORIENTATION_0:
+                    default:
+                        return Surface.ROTATION_0;
+                }
+            }
+        } catch (Exception e) {
+            // Use default value if we can't talk to config store.
+        }
+        return Surface.ROTATION_0;
+    }
+
     void reportFocusChanged(IBinder oldToken, IBinder newToken) {
         WindowState lastFocus;
         WindowState newFocus;
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 4fb801e..8369319 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -35,6 +35,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -146,7 +147,7 @@
                     connectNativeService();
                     break;
                 default:
-                    throw new AssertionError("Unknown message: " + message.toString());
+                    throw new AssertionError("Unknown message: " + message);
             }
         }
     }
@@ -190,11 +191,14 @@
                 Log.d(LOG_TAG, "Starting background process job");
             }
 
-            try {
-                sSelfService.mIProfcollect.process(false);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, e.getMessage());
-            }
+            BackgroundThread.get().getThreadHandler().post(
+                    () -> {
+                        try {
+                            sSelfService.mIProfcollect.process();
+                        } catch (RemoteException e) {
+                            Log.e(LOG_TAG, e.getMessage());
+                        }
+                    });
             return true;
         }
 
@@ -301,7 +305,7 @@
         }
 
         Context context = getContext();
-        new Thread(() -> {
+        BackgroundThread.get().getThreadHandler().post(() -> {
             try {
                 // Prepare profile report
                 String reportName = mIProfcollect.report() + ".zip";
@@ -321,6 +325,6 @@
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, e.getMessage());
             }
-        }).start();
+        });
     }
 }
diff --git a/services/tests/mockingservicestests/OWNERS b/services/tests/mockingservicestests/OWNERS
new file mode 100644
index 0000000..0fb0c30
--- /dev/null
+++ b/services/tests/mockingservicestests/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
diff --git a/services/tests/servicestests/OWNERS b/services/tests/servicestests/OWNERS
new file mode 100644
index 0000000..0fb0c30
--- /dev/null
+++ b/services/tests/servicestests/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 5c53d43..9e1445c 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -26,11 +26,11 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioManager;
 import android.media.AudioSystem;
+import android.media.BtProfileConnectionInfo;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -98,16 +98,12 @@
         Log.i(TAG, "starting testPostA2dpDeviceConnectionChange");
         Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
 
-        mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-                new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
-                        BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1));
+        mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+                new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
+                    BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource"));
         Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS);
-        verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState(
-                any(BluetoothDevice.class),
-                ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/,
-                ArgumentMatchers.eq(BluetoothProfile.A2DP) /*profile*/,
-                ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/,
-                ArgumentMatchers.eq(1) /*a2dpVolume*/
+        verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice(
+                any(AudioDeviceBroker.BtDeviceInfo.class)
         );
 
         // verify the connection was reported to AudioSystem
@@ -210,30 +206,29 @@
         ((NoOpAudioSystemAdapter) mSpyAudioSystem).configureIsStreamActive(mockMediaPlayback);
 
         // first connection: ensure the device is connected as a starting condition for the test
-        mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-                new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
-                        BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1));
+        mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+                new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
+                    BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource"));
         Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
 
         // disconnection
-        mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-                new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
-                        BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1));
+        mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+                new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice,
+                    BtProfileConnectionInfo.a2dpInfo(false, -1), "testSource"));
         if (delayAfterDisconnection > 0) {
             Thread.sleep(delayAfterDisconnection);
         }
 
         // reconnection
-        mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-                new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
-                        BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2));
+        mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
+                new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
+                    BtProfileConnectionInfo.a2dpInfo(true, 2), "testSource"));
         Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS);
 
         // Verify disconnection has been cancelled and we're seeing two connections attempts,
         // with the device connected at the end of the test
-        verify(mSpyDevInventory, times(2)).onSetA2dpSinkConnectionState(
-                any(BtHelper.BluetoothA2dpDeviceInfo.class),
-                ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED));
+        verify(mSpyDevInventory, times(2)).onSetBtActiveDevice(
+                any(AudioDeviceBroker.BtDeviceInfo.class), anyInt());
         Assert.assertTrue("Mock device not connected",
                 mSpyDevInventory.isA2dpDeviceConnected(mFakeBtDevice));
 
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index cac716e..0ddd52d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -69,7 +69,14 @@
  * them know that the app has crashed and that their call was continued using the pre-loaded dialer
  * app.
  * <p>
- * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call.
+ * The pre-loaded dialer will ALWAYS be used when the user places an emergency call, even if your
+ * app fills the {@link android.app.role.RoleManager#ROLE_DIALER} role.  To ensure an optimal
+ * experience when placing an emergency call, the default dialer should ALWAYS use
+ * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls (including
+ * emergency calls).  This ensures that the platform is able to verify that the request came from
+ * the default dialer.  If a non-preloaded dialer app uses {@link Intent#ACTION_CALL} to place an
+ * emergency call, it will be raised to the preloaded dialer app using {@link Intent#ACTION_DIAL}
+ * for confirmation; this is a suboptimal user experience.
  * <p>
  * Below is an example manifest registration for an {@code InCallService}. The meta-data
  * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index ae597e0..2b355ae 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -16,11 +16,14 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.RadioAccessSpecifier;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -32,7 +35,6 @@
  * Network Service when passed through {@link TelephonyManager#updateAvailableNetworks}
  */
 public final class AvailableNetworkInfo implements Parcelable {
-
     /*
      * Defines number of priority level high.
      */
@@ -48,6 +50,14 @@
      */
     public static final int PRIORITY_LOW = 3;
 
+    /** @hide */
+    @IntDef({
+        PRIORITY_HIGH,
+        PRIORITY_MED,
+        PRIORITY_LOW,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AvailableNetworkInfoPriority {}
     /**
      * subscription Id of the available network. This value must be one of the entry retrieved from
      * {@link SubscriptionManager#getOpportunisticSubscriptions}
@@ -62,7 +72,7 @@
      * for network selection. If there are more than one subId with highest priority then the
      * network with highest RSRP is chosen.
      */
-    private int mPriority;
+    private @AvailableNetworkInfoPriority int mPriority;
 
     /**
      * Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
@@ -77,8 +87,7 @@
      * Opportunistic network service will use these bands to scan.
      *
      * When no specific bands are specified (empty array or null) CBRS band
-     * {@link AccessNetworkConstants.EutranBand.BAND_48
-     * } will be used for network scan.
+     * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
      *
      * See {@link AccessNetworkConstants} for details.
      *
@@ -94,7 +103,7 @@
      * If this entry is left empty, {@link RadioAcccessSpecifier}s with {@link AccessNetworkType}s
      * of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
      * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
-     * by Opportunistic network service.
+     * by Opportunistic network service for a network scan.
      */
     private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers;
 
@@ -117,6 +126,7 @@
      * network with highest RSRP is chosen.
      * @return priority level
      */
+    @AvailableNetworkInfoPriority
     public int getPriority() {
         return mPriority;
     }
@@ -149,15 +159,9 @@
      * Returns a list of {@link RadioAccessSpecifier} associated with the available network.
      * Opportunistic network service will use this to determine which bands to scan for.
      *
-     * the returned value is one of {@link AccessNetworkConstants.AccessNetworkType}. When no
-     * specific access network type is specified, {@link RadioAccessSpecifier}s with {@link
-     * AccessNetworkType}s of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
-     * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
-     * by Opportunistic network service.
      * @return the access network type associated with the available network.
-     * @hide
      */
-    public List<RadioAccessSpecifier>  getRadioAccessSpecifiers() {
+    public @NonNull List<RadioAccessSpecifier> getRadioAccessSpecifiers() {
         return (List<RadioAccessSpecifier>) mRadioAccessSpecifiers.clone();
     }
 
@@ -193,9 +197,9 @@
     }
 
     /** @hide */
-    private AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
-            @NonNull List<Integer> bands, @NonNull List<RadioAccessSpecifier>
-            radioAccessSpecifiers) {
+    private AvailableNetworkInfo(int subId, @AvailableNetworkInfoPriority int priority,
+            @NonNull List<String> mccMncs, @NonNull List<Integer> bands,
+            @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
         mSubId = subId;
         mPriority = priority;
         mMccMncs = new ArrayList<String>(mccMncs);
@@ -261,27 +265,39 @@
      *
      * <pre><code>
      *
-     * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder()
-     *     .setSubId(1)
+     * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder(subId)
      *     .setPriority(AvailableNetworkInfo.PRIORITY_MED)
+     *     .setRadioAccessSpecifiers(radioAccessSpecifiers)
+     *     .setMccMncs(mccMncs)
      *     .build();
      * </code></pre>
-     *
-     * @hide
      */
     public static final class Builder {
         private int mSubId = Integer.MIN_VALUE;
-        private int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
+        private @AvailableNetworkInfoPriority int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
         private ArrayList<String> mMccMncs = new ArrayList<>();
-        private ArrayList<Integer> mBands = new ArrayList<>();
         private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers = new ArrayList<>();
 
-        public @NonNull Builder setSubId(int subId) {
+        /**
+         *
+         */
+        /**
+         * Creates an AvailableNetworkInfo Builder with specified subscription id.
+         *
+         * @param subId of the availableNetwork.
+         */
+        public Builder(int subId) {
             mSubId = subId;
-            return this;
         }
 
-        public @NonNull Builder setPriority(int priority) {
+        /**
+         * Sets the priority for the subscription id.
+         *
+         * @param priority of the subscription id. See {@link AvailableNetworkInfo#getPriority} for
+         * more details
+         * @return the original Builder object.
+         */
+        public @NonNull Builder setPriority(@AvailableNetworkInfoPriority int priority) {
             if (priority > AvailableNetworkInfo.PRIORITY_LOW
                     || priority < AvailableNetworkInfo.PRIORITY_HIGH) {
                 throw new IllegalArgumentException("A valid priority must be set");
@@ -290,30 +306,48 @@
             return this;
         }
 
-        public @NonNull Builder setMccMncs(@NonNull ArrayList<String> mccMncs) {
-            Objects.requireNonNull(mccMncs, "A non-null ArrayList of mccmncs must be set. An empty "
-                    + "list is still accepted. Please read documentation in "
-                    + "AvailableNetworkService to see consequences of an empty Arraylist.");
-            mMccMncs = mccMncs;
+        /**
+         * Sets the list of mccmncs associated with the subscription id.
+         *
+         * @param mccMncs nonull list of mccmncs. An empty List is still accepted. Please read
+         * documentation in {@link AvailableNetworkInfo} to see consequences of an empty List.
+         * @return the original Builder object.
+         */
+        public @NonNull Builder setMccMncs(@NonNull List<String> mccMncs) {
+            Objects.requireNonNull(mccMncs, "A non-null List of mccmncs must be set. An empty "
+                    + "List is still accepted. Please read documentation in "
+                    + "AvailableNetworkInfo to see consequences of an empty List.");
+            mMccMncs = new ArrayList<>(mccMncs);
             return this;
         }
 
+        /**
+         * Sets the list of mccmncs associated with the subscription id.
+         *
+         * @param radioAccessSpecifiers nonull list of radioAccessSpecifiers. An empty List is still
+         * accepted. Please read documentation in {@link AvailableNetworkInfo} to see
+         * consequences of an empty List.
+         * @return the original Builder object.
+         */
         public @NonNull Builder setRadioAccessSpecifiers(
-                @NonNull ArrayList<RadioAccessSpecifier> radioAccessSpecifiers) {
-            Objects.requireNonNull(radioAccessSpecifiers, "A non-null ArrayList of "
-                    + "RadioAccessSpecifiers must be set. An empty list is still accepted. Please "
-                    + "read documentation in AvailableNetworkService to see consequences of an "
-                    + "empty Arraylist.");
-            mRadioAccessSpecifiers = radioAccessSpecifiers;
+                @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
+            Objects.requireNonNull(radioAccessSpecifiers, "A non-null List of "
+                    + "RadioAccessSpecifiers must be set. An empty List is still accepted. Please "
+                    + "read documentation in AvailableNetworkInfo to see consequences of an "
+                    + "empty List.");
+            mRadioAccessSpecifiers = new ArrayList<>(radioAccessSpecifiers);
             return this;
         }
 
+        /**
+         * @return an AvailableNetworkInfo object with all the fields previously set by the Builder.
+         */
         public @NonNull AvailableNetworkInfo build() {
             if (mSubId == Integer.MIN_VALUE) {
                 throw new IllegalArgumentException("A valid subId must be set");
             }
 
-            return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, mBands,
+            return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, new ArrayList<>(),
                     mRadioAccessSpecifiers);
         }
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 73a3082..894bb8e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3675,6 +3675,49 @@
             "show_wifi_calling_icon_in_status_bar_bool";
 
     /**
+     * Configuration to indicate that the carrier supports opportunistic data
+     * auto provisioning. Based on this flag, the device downloads and activates
+     * corresponding opportunistic profile.
+     */
+    public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL =
+            "carrier_supports_opp_data_auto_provisioning_bool";
+
+    /**
+     * SMDP+ server address for downloading opportunistic eSIM profile.
+     * FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the
+     * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 [15] excluding '$'.
+     */
+    public static final String KEY_SMDP_SERVER_ADDRESS_STRING =
+            "smdp_server_address_string";
+
+    /**
+     * This timer value is used in the eSIM Exponential Backoff download retry algorithm.
+     * Value should be in seconds.
+     * <OL>
+     *     <LI>When the first download failure occurs, retry download after BACKOFF_TIMER_VALUE
+     * seconds.</LI>
+     *
+     * <LI>If download fails again then, retry after either BACKOFF_TIMER_VALUE,
+     * 2xBACKOFF_TIMER_VALUE, or 3xBACKOFF_TIMER_VALUE seconds.</LI>
+     *
+     * <LI>In general after the cth failed attempt, retry after k * BACKOFF_TIMER_VALUE
+     * seconds, where k is a random integer between 1 and 2^c − 1. Max c value is
+     * {@link #KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT}</LI>
+     * </OL>
+     */
+    public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT =
+            "esim_download_retry_backoff_timer_sec_int";
+
+    /**
+     * If eSIM profile download fails then, the number of retry attempts by UE
+     * will be based on this configuration. If download still fails even after the
+     * MAX attempts configured by this item then the retry is postponed until next
+     * device bootup.
+     */
+    public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT =
+            "esim_max_download_retry_attempts_int";
+
+    /**
      * Controls RSRP threshold at which OpportunisticNetworkService will decide whether
      * the opportunistic network is good enough for internet data.
      */
@@ -3884,6 +3927,30 @@
     public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
             "enabled_4g_opportunistic_network_scan_bool";
 
+  /**
+   * Only relevant when the device supports opportunistic networks but does not support
+   * simultaneuous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network
+   * goes out of service before switching the 5G capability back to primary stack. The idea of
+   * waiting a few seconds is to minimize the calling of the expensive capability switching
+   * operation in the case where CBRS goes back into service shortly after going out of it.
+   *
+   * @hide
+   */
+  public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG =
+            "time_to_switch_back_to_primary_if_opportunistic_oos_long";
+
+  /**
+   * Only relevant when the device supports opportunistic networks but does not support
+   * simultaneuous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back
+   * to primary stack due to opportunistic network being OOS. The idea is to minimizing the
+   * 'ping-ponging' effect where device is constantly witching capability back and forth between
+   * primary and opportunistic stack.
+   *
+   * @hide
+   */
+  public static final String KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG
+          = "opportunistic_time_to_scan_after_capability_switch_to_primary_long";
+
     /**
      * Indicates zero or more emergency number prefix(es), because some carrier requires
      * if users dial an emergency number address with a specific prefix, the combination of the
@@ -5202,6 +5269,16 @@
     public static final String KEY_VONR_SETTING_VISIBILITY_BOOL = "vonr_setting_visibility_bool";
 
     /**
+     * Flag specifying whether VoNR should be enabled for carrier.
+     * If true, VoNr will be enabled. If false, hard disabled.
+     *
+     * Disabled by default.
+     *
+     * @hide
+     */
+    public static final String KEY_VONR_ENABLED_BOOL = "vonr_enabled_bool";
+
+    /**
      * Determine whether unthrottle data retry when tracking area code (TAC/LAC) from cell changes
      *
      * @hide
@@ -5703,6 +5780,10 @@
         sDefaults.putBoolean(KEY_UNMETERED_NR_SA_SUB6_BOOL, false);
         sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_WIFI_CALLING_ICON_IN_STATUS_BAR_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL, false);
+        sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, "");
+        sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
+        sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
         sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
@@ -5746,6 +5827,10 @@
         /* Default value is 2 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
         sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
+        sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000);
+        sDefaults.putInt(
+                KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
+                120000);
         sDefaults.putAll(Gps.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
                 new int[] {
@@ -5818,6 +5903,7 @@
         sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false);
         sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
         sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, false);
+        sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3b44a34..d5315ac 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -949,6 +949,15 @@
     public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS;
 
     /**
+     * TelephonyProvider column name for NR Advanced calling
+     * Determines if the user has enabled VoNR settings for this subscription.
+     *
+     * @hide
+     */
+    public static final String NR_ADVANCED_CALLING_ENABLED =
+            SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED;
+
+    /**
      * Profile class of the subscription
      * @hide
      */
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 5d57de6..be09545 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -154,7 +154,7 @@
   const char* end = path.end();
   const char* last_dir_sep = path.begin();
   for (const char* c = path.begin(); c != end; ++c) {
-    if (*c == sDirSep) {
+    if (*c == sDirSep || *c == sInvariantDirSep) {
       last_dir_sep = c + 1;
     }
   }
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 481a4cd..e50cb50 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -41,6 +41,8 @@
 constexpr const char sPathSep = ':';
 #endif
 
+constexpr const char sInvariantDirSep = '/';
+
 enum class FileType {
   kUnknown = 0,
   kNonexistant,