Merge "Add flag a2dp_lhdc_api" into main
diff --git a/Android.bp b/Android.bp
index fde5137..9142bd5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -189,10 +189,9 @@
 java_defaults {
     name: "bluetooth_framework_errorprone_rules",
     defaults: ["bluetooth_errorprone_rules"],
-    plugins: [
-        "error_prone_android_framework",
-    ],
     errorprone: {
+        extra_check_modules: ["error_prone_android_framework"],
+
         javacflags: [
             "-Xep:AndroidFrameworkBinderIdentity:ERROR",
             "-Xep:AndroidFrameworkBluetoothPermission:ERROR",
diff --git a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/DistanceMeasurementInitiator.java b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/DistanceMeasurementInitiator.java
index 9d39dcb..1dc7533 100644
--- a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/DistanceMeasurementInitiator.java
+++ b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/DistanceMeasurementInitiator.java
@@ -38,6 +38,34 @@
 
 class DistanceMeasurementInitiator {
 
+    enum Freq {
+        HIGH(DistanceMeasurementParams.REPORT_FREQUENCY_HIGH),
+        MEDIUM(DistanceMeasurementParams.REPORT_FREQUENCY_MEDIUM),
+        LOW(DistanceMeasurementParams.REPORT_FREQUENCY_LOW);
+        private final int freq;
+
+        Freq(int freq) {
+            this.freq = freq;
+        }
+
+        int getFreq() {
+            return freq;
+        }
+
+        @Override
+        public String toString() {
+            return name();
+        }
+
+        public static Freq fromName(String name) {
+            try {
+                return Freq.valueOf(name);
+            } catch (IllegalArgumentException e) {
+                return MEDIUM;
+            }
+        }
+    }
+
     private static final int DISTANCE_MEASUREMENT_DURATION_SEC = 3600;
     private static final List<Pair<Integer, String>> mDistanceMeasurementMethodMapping =
             List.of(
@@ -119,8 +147,12 @@
         return methods;
     }
 
+    List<String> getMeasurementFreqs() {
+        return List.of(Freq.MEDIUM.toString(), Freq.HIGH.toString(), Freq.LOW.toString());
+    }
+
     @SuppressLint("MissingPermission") // permissions are checked upfront
-    void startDistanceMeasurement(String distanceMeasurementMethodName) {
+    void startDistanceMeasurement(String distanceMeasurementMethodName, String selectedFreq) {
 
         if (mTargetDevice == null) {
             printLog("do Gatt connect first");
@@ -132,7 +164,7 @@
         DistanceMeasurementParams params =
                 new DistanceMeasurementParams.Builder(mTargetDevice)
                         .setDurationSeconds(DISTANCE_MEASUREMENT_DURATION_SEC)
-                        .setFrequency(DistanceMeasurementParams.REPORT_FREQUENCY_LOW)
+                        .setFrequency(Freq.fromName(selectedFreq).getFreq())
                         .setMethodId(getDistanceMeasurementMethodId(distanceMeasurementMethodName))
                         .build();
         DistanceMeasurementManager distanceMeasurementManager =
diff --git a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorFragment.java b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorFragment.java
index fe3a211..b1b96d7 100644
--- a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorFragment.java
+++ b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorFragment.java
@@ -43,9 +43,11 @@
     private static final DecimalFormat DISTANCE_DECIMAL_FMT = new DecimalFormat("0.00");
 
     private ArrayAdapter<String> mDmMethodArrayAdapter;
+    private ArrayAdapter<String> mFreqArrayAdapter;
     private TextView mDistanceText;
     private CanvasView mDistanceCanvasView;
     private Spinner mSpinnerDmMethod;
+    private Spinner mSpinnerFreq;
     private Button mButtonCs;
     private LinearLayout mDistanceViewLayout;
     private TextView mLogText;
@@ -63,6 +65,7 @@
 
         mButtonCs = (Button) root.findViewById(R.id.btn_cs);
         mSpinnerDmMethod = (Spinner) root.findViewById(R.id.spinner_dm_method);
+        mSpinnerFreq = (Spinner) root.findViewById(R.id.spinner_freq);
         mDistanceViewLayout = (LinearLayout) root.findViewById(R.id.layout_distance_view);
         mDistanceText = new TextView(getContext());
         mDistanceViewLayout.addView(mDistanceText);
@@ -85,6 +88,11 @@
         mDmMethodArrayAdapter.setDropDownViewResource(
                 android.R.layout.simple_spinner_dropdown_item);
         mSpinnerDmMethod.setAdapter(mDmMethodArrayAdapter);
+        mFreqArrayAdapter =
+                new ArrayAdapter<String>(
+                        getContext(), android.R.layout.simple_spinner_item, new ArrayList<>());
+        mFreqArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mSpinnerFreq.setAdapter(mFreqArrayAdapter);
 
         mInitiatorViewModel = new ViewModelProvider(this).get(InitiatorViewModel.class);
         mBleConnectionViewModel = new ViewModelProvider(this).get(BleConnectionViewModel.class);
@@ -134,14 +142,15 @@
                         });
 
         mDmMethodArrayAdapter.addAll(mInitiatorViewModel.getSupportedDmMethods());
-
+        mFreqArrayAdapter.addAll(mInitiatorViewModel.getMeasurementFreqs());
         mButtonCs.setOnClickListener(
                 v -> {
                     String methodName = mSpinnerDmMethod.getSelectedItem().toString();
+                    String freq = mSpinnerFreq.getSelectedItem().toString();
                     if (TextUtils.isEmpty(methodName)) {
                         printLog("the device doesn't support any distance measurement methods.");
                     }
-                    mInitiatorViewModel.toggleCsStartStop(methodName);
+                    mInitiatorViewModel.toggleCsStartStop(methodName, freq);
                 });
     }
 
diff --git a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorViewModel.java b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorViewModel.java
index f54ec7b..e07e7a1 100644
--- a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorViewModel.java
+++ b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorViewModel.java
@@ -71,9 +71,14 @@
         return mDistanceMeasurementInitiator.getDistanceMeasurementMethods();
     }
 
-    void toggleCsStartStop(String distanceMeasurementMethodName) {
+    List<String> getMeasurementFreqs() {
+        return mDistanceMeasurementInitiator.getMeasurementFreqs();
+    }
+
+    void toggleCsStartStop(String distanceMeasurementMethodName, String freq) {
         if (!mCsStarted.getValue()) {
-            mDistanceMeasurementInitiator.startDistanceMeasurement(distanceMeasurementMethodName);
+            mDistanceMeasurementInitiator.startDistanceMeasurement(
+                    distanceMeasurementMethodName, freq);
         } else {
             mDistanceMeasurementInitiator.stopDistanceMeasurement();
         }
diff --git a/android/ChannelSoundingTestApp/app/src/main/res/layout/fragment_initiator.xml b/android/ChannelSoundingTestApp/app/src/main/res/layout/fragment_initiator.xml
index f5f57f2..39b0246 100644
--- a/android/ChannelSoundingTestApp/app/src/main/res/layout/fragment_initiator.xml
+++ b/android/ChannelSoundingTestApp/app/src/main/res/layout/fragment_initiator.xml
@@ -21,15 +21,43 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
 
-
+        <TextView
+            android:id="@+id/dm_method_label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text= "Method"
+            app:layout_constraintBottom_toTopOf="@id/btn_cs"
+            app:layout_constraintEnd_toStartOf="@id/spinner_dm_method"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container"
+            />
         <Spinner
             android:id="@+id/spinner_dm_method"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginTop="8dp"
             android:padding="10dp"
-            app:layout_constraintEnd_toStartOf="@id/btn_cs"
-            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/freq_label"
+            app:layout_constraintStart_toEndOf="@id/dm_method_label"
+            app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container" />
+        <TextView
+            android:id="@+id/freq_label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text= "Freq"
+            app:layout_constraintBottom_toTopOf="@id/btn_cs"
+            app:layout_constraintEnd_toStartOf="@id/spinner_freq"
+            app:layout_constraintStart_toEndOf="@id/spinner_dm_method"
+            app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container"
+            />
+        <Spinner
+            android:id="@+id/spinner_freq"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:padding="10dp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@id/freq_label"
             app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container" />
         <Button
             android:id="@+id/btn_cs"
@@ -40,8 +68,8 @@
             android:padding="10dp"
             android:text="Start Distance Measurement"
             app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toEndOf="@id/spinner_dm_method"
-            app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container" />
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/spinner_dm_method" />
         <TextView
             android:id="@+id/text_log"
             android:layout_width="match_parent"
diff --git a/android/app/Android.bp b/android/app/Android.bp
index e8ca967..84de551 100644
--- a/android/app/Android.bp
+++ b/android/app/Android.bp
@@ -259,6 +259,7 @@
         "BluetoothApiShims",
         "android.hardware.radio-V1.0-java",
         "android.hardware.radio.sap-V1-java",
+        "android.media.audio-aconfig-exported-java",
         "androidx.annotation_annotation",
         "androidx.core_core",
         "androidx.lifecycle_lifecycle-livedata",
@@ -283,7 +284,6 @@
 
     plugins: [
         "androidx.room_room-compiler-plugin",
-        "error_prone_android_framework",
     ],
 
     // Export schemas to the test directory so that we have an history
diff --git a/android/app/AndroidManifest.xml b/android/app/AndroidManifest.xml
index 3416c8f..3b27e4b 100644
--- a/android/app/AndroidManifest.xml
+++ b/android/app/AndroidManifest.xml
@@ -295,7 +295,7 @@
         </activity>
 
         <service android:process="@string/process"
-             android:name="com.android.bluetooth.pbapclient.AuthenticationService"
+             android:name="com.android.bluetooth.pbapclient.PbapClientAccountAuthenticatorService"
              android:enabled="false"
              android:exported="true">
             <intent-filter>
diff --git a/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl b/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl
index e09f6e9..f9436a7 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl
@@ -192,4 +192,6 @@
     int getChannelSoundingMaxSupportedSecurityLevel(in BluetoothDevice remoteDevice, in AttributionSource attributionSource);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     int getLocalChannelSoundingMaxSupportedSecurityLevel(in AttributionSource attributionSource);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    int[] getChannelSoundingSupportedSecurityLevels(in AttributionSource attributionSource);
 }
diff --git a/android/app/aidl/android/bluetooth/IBluetoothLeAudio.aidl b/android/app/aidl/android/bluetooth/IBluetoothLeAudio.aidl
index f026d2c..795650b 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothLeAudio.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothLeAudio.aidl
@@ -60,6 +60,10 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     void setCodecConfigPreference(in int groupId, in BluetoothLeAudioCodecConfig inputCodecConfig, in BluetoothLeAudioCodecConfig outputCodecConfig, in AttributionSource source);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    void setBroadcastToUnicastFallbackGroup(in int groupId, in AttributionSource attributionSource);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    int getBroadcastToUnicastFallbackGroup(in AttributionSource attributionSource);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     oneway void registerCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     oneway void unregisterCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource);
diff --git a/android/app/jni/com_android_bluetooth_avrcp_controller.cpp b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
index a0b80f7..5150a01 100644
--- a/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
+++ b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
@@ -779,7 +779,7 @@
     return JNI_FALSE;
   }
 
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
 
   log::info("key_code: {}, key_state: {}", key_code, key_state);
 
@@ -808,7 +808,7 @@
     return JNI_FALSE;
   }
 
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
 
   log::info("key_code: {}, key_state: {}", key_code, key_state);
 
@@ -833,7 +833,7 @@
 static void setPlayerApplicationSettingValuesNative(JNIEnv* env, jobject /* object */,
                                                     jbyteArray address, jbyte num_attrib,
                                                     jbyteArray attrib_ids, jbyteArray attrib_val) {
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   if (!sBluetoothAvrcpInterface) {
     return;
   }
@@ -893,7 +893,7 @@
     return;
   }
 
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
@@ -916,7 +916,7 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
@@ -939,7 +939,7 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
-  log::verbose("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::verbose("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
@@ -961,7 +961,7 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
-  log::verbose("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::verbose("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
@@ -982,7 +982,7 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
-  log::verbose("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::verbose("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
@@ -1004,7 +1004,7 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
-  log::verbose("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::verbose("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
@@ -1025,7 +1025,7 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
@@ -1053,7 +1053,7 @@
   //  return;
   //}
 
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
@@ -1077,7 +1077,7 @@
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_cmd(rawAddress, (uint16_t)id);
   if (status != BT_STATUS_SUCCESS) {
     log::error("Failed sending setBrowsedPlayerNative command, status: {}", bt_status_text(status));
@@ -1098,7 +1098,7 @@
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_cmd(rawAddress, (uint16_t)id);
   if (status != BT_STATUS_SUCCESS) {
     log::error("Failed sending setAddressedPlayerNative command, status: {}",
@@ -1126,7 +1126,7 @@
   RawAddress rawAddress;
   rawAddress.FromOctets((uint8_t*)addr);
 
-  log::info("sBluetoothAvrcpInterface: {}", fmt::ptr(sBluetoothAvrcpInterface));
+  log::info("sBluetoothAvrcpInterface: {}", std::format_ptr(sBluetoothAvrcpInterface));
   bt_status_t status = sBluetoothAvrcpInterface->play_item_cmd(
           rawAddress, (uint8_t)scope, (uint8_t*)&uid, (uint16_t)uidCounter);
   if (status != BT_STATUS_SUCCESS) {
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index da4b61e..0d31f17 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -49,12 +49,12 @@
 using bluetooth::Uuid;
 extern bt_interface_t bluetoothInterface;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bt_state_t> : enum_formatter<bt_state_t> {};
 template <>
 struct formatter<bt_discovery_state_t> : enum_formatter<bt_discovery_state_t> {};
-}  // namespace fmt
+}  // namespace std
 
 static Uuid from_java_uuid(jlong uuid_msb, jlong uuid_lsb) {
   std::array<uint8_t, Uuid::kNumBytes128> uu;
@@ -825,7 +825,7 @@
     vm->AttachCurrentThread(&callbackEnv, &args);
     sHaveCallbackThread = true;
     sCallbackThread = pthread_self();
-    log::verbose("Callback thread attached: {}", fmt::ptr(callbackEnv));
+    log::verbose("Callback thread attached: {}", std::format_ptr(callbackEnv));
   } else if (event == DISASSOCIATE_JVM) {
     if (!isCallbackThread()) {
       log::error("Callback: '' is not called on the correct thread");
diff --git a/android/app/lint-baseline.xml b/android/app/lint-baseline.xml
index c972c11..5bd3f2f 100644
--- a/android/app/lint-baseline.xml
+++ b/android/app/lint-baseline.xml
@@ -359,7 +359,7 @@
         errorLine1="                SimpleDateFormat parser = new SimpleDateFormat(TIMESTAMP_FORMAT);"
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/pbapclient/CallLogPullRequest.java"
+            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/pbapclient/obex/CallLogPullRequest.java"
             line="105"
             column="43"/>
     </issue>
diff --git a/android/app/res/values-af/strings_pbap_client.xml b/android/app/res/values-af/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-af/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-am/strings_pbap_client.xml b/android/app/res/values-am/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-am/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ar/strings_pbap_client.xml b/android/app/res/values-ar/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ar/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-as/strings_pbap_client.xml b/android/app/res/values-as/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-as/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-az/strings_pbap_client.xml b/android/app/res/values-az/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-az/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-b+sr+Latn/strings_pbap_client.xml b/android/app/res/values-b+sr+Latn/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-b+sr+Latn/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-be/strings_pbap_client.xml b/android/app/res/values-be/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-be/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-bg/strings_pbap_client.xml b/android/app/res/values-bg/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-bg/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-bn/strings_pbap_client.xml b/android/app/res/values-bn/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-bn/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-bs/strings_pbap_client.xml b/android/app/res/values-bs/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-bs/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ca/strings_pbap_client.xml b/android/app/res/values-ca/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ca/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-cs/strings_pbap_client.xml b/android/app/res/values-cs/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-cs/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-da/strings_pbap_client.xml b/android/app/res/values-da/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-da/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-de/strings_pbap_client.xml b/android/app/res/values-de/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-de/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-el/strings_pbap_client.xml b/android/app/res/values-el/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-el/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-en-rAU/strings_pbap_client.xml b/android/app/res/values-en-rAU/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-en-rAU/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-en-rCA/strings_pbap_client.xml b/android/app/res/values-en-rCA/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-en-rCA/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-en-rGB/strings_pbap_client.xml b/android/app/res/values-en-rGB/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-en-rGB/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-en-rIN/strings_pbap_client.xml b/android/app/res/values-en-rIN/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-en-rIN/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-en-rXC/strings_pbap_client.xml b/android/app/res/values-en-rXC/strings_pbap_client.xml
deleted file mode 100644
index b27d984..0000000
--- a/android/app/res/values-en-rXC/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎com.android.bluetooth.pbapsink‎‏‎‎‏‎"</string>
-</resources>
diff --git a/android/app/res/values-es-rUS/strings_pbap_client.xml b/android/app/res/values-es-rUS/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-es-rUS/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-es/strings_pbap_client.xml b/android/app/res/values-es/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-es/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-et/strings_pbap_client.xml b/android/app/res/values-et/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-et/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-eu/strings_pbap_client.xml b/android/app/res/values-eu/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-eu/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-fa/strings_pbap_client.xml b/android/app/res/values-fa/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-fa/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-fi/strings_pbap_client.xml b/android/app/res/values-fi/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-fi/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-fr-rCA/strings_pbap_client.xml b/android/app/res/values-fr-rCA/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-fr-rCA/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-fr/strings_pbap_client.xml b/android/app/res/values-fr/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-fr/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-gl/strings_pbap_client.xml b/android/app/res/values-gl/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-gl/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-gu/strings_pbap_client.xml b/android/app/res/values-gu/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-gu/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-hi/strings_pbap_client.xml b/android/app/res/values-hi/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-hi/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-hr/strings_pbap_client.xml b/android/app/res/values-hr/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-hr/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-hu/strings_pbap_client.xml b/android/app/res/values-hu/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-hu/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-hy/strings_pbap_client.xml b/android/app/res/values-hy/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-hy/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-in/strings_pbap_client.xml b/android/app/res/values-in/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-in/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-is/strings_pbap_client.xml b/android/app/res/values-is/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-is/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-it/strings_pbap_client.xml b/android/app/res/values-it/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-it/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-iw/strings_pbap_client.xml b/android/app/res/values-iw/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-iw/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ja/strings_pbap_client.xml b/android/app/res/values-ja/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ja/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ka/strings_pbap_client.xml b/android/app/res/values-ka/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ka/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-kk/strings_pbap_client.xml b/android/app/res/values-kk/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-kk/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-km/strings_pbap_client.xml b/android/app/res/values-km/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-km/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-kn/strings_pbap_client.xml b/android/app/res/values-kn/strings_pbap_client.xml
deleted file mode 100644
index 8aceb64..0000000
--- a/android/app/res/values-kn/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.ಬ್ಲೂಟೂತ್.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ko/strings_pbap_client.xml b/android/app/res/values-ko/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ko/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ky/strings_pbap_client.xml b/android/app/res/values-ky/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ky/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-lo/strings_pbap_client.xml b/android/app/res/values-lo/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-lo/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-lt/strings_pbap_client.xml b/android/app/res/values-lt/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-lt/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-lv/strings_pbap_client.xml b/android/app/res/values-lv/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-lv/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-mk/strings_pbap_client.xml b/android/app/res/values-mk/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-mk/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ml/strings_pbap_client.xml b/android/app/res/values-ml/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ml/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-mn/strings_pbap_client.xml b/android/app/res/values-mn/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-mn/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-mr/strings_pbap_client.xml b/android/app/res/values-mr/strings_pbap_client.xml
deleted file mode 100644
index 0ad7629..0000000
--- a/android/app/res/values-mr/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.ब्लूटूथ.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ms/strings_pbap_client.xml b/android/app/res/values-ms/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ms/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-my/strings_pbap_client.xml b/android/app/res/values-my/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-my/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-nb/strings_pbap_client.xml b/android/app/res/values-nb/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-nb/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ne/strings_pbap_client.xml b/android/app/res/values-ne/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ne/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-nl/strings_pbap_client.xml b/android/app/res/values-nl/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-nl/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-or/strings_pbap_client.xml b/android/app/res/values-or/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-or/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-pa/strings_pbap_client.xml b/android/app/res/values-pa/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-pa/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-pl/strings_pbap_client.xml b/android/app/res/values-pl/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-pl/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-pt-rPT/strings_pbap_client.xml b/android/app/res/values-pt-rPT/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-pt-rPT/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-pt/strings_pbap_client.xml b/android/app/res/values-pt/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-pt/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ro/strings_pbap_client.xml b/android/app/res/values-ro/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ro/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ru/strings_pbap_client.xml b/android/app/res/values-ru/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ru/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-si/strings_pbap_client.xml b/android/app/res/values-si/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-si/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-sk/strings_pbap_client.xml b/android/app/res/values-sk/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-sk/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-sl/strings_pbap_client.xml b/android/app/res/values-sl/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-sl/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-sq/strings_pbap_client.xml b/android/app/res/values-sq/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-sq/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-sr/strings_pbap_client.xml b/android/app/res/values-sr/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-sr/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-sv/strings_pbap_client.xml b/android/app/res/values-sv/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-sv/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-sw/strings_pbap_client.xml b/android/app/res/values-sw/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-sw/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ta/strings_pbap_client.xml b/android/app/res/values-ta/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ta/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-te/strings_pbap_client.xml b/android/app/res/values-te/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-te/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-th/strings_pbap_client.xml b/android/app/res/values-th/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-th/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-tl/strings_pbap_client.xml b/android/app/res/values-tl/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-tl/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-tr/strings_pbap_client.xml b/android/app/res/values-tr/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-tr/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-uk/strings_pbap_client.xml b/android/app/res/values-uk/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-uk/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-ur/strings_pbap_client.xml b/android/app/res/values-ur/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-ur/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-uz/strings_pbap_client.xml b/android/app/res/values-uz/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-uz/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-vi/strings_pbap_client.xml b/android/app/res/values-vi/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-vi/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-zh-rCN/strings_pbap_client.xml b/android/app/res/values-zh-rCN/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-zh-rCN/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-zh-rHK/strings_pbap_client.xml b/android/app/res/values-zh-rHK/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-zh-rHK/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-zh-rTW/strings_pbap_client.xml b/android/app/res/values-zh-rTW/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-zh-rTW/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values-zu/strings_pbap_client.xml b/android/app/res/values-zu/strings_pbap_client.xml
deleted file mode 100644
index 57ca2ef..0000000
--- a/android/app/res/values-zu/strings_pbap_client.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type" msgid="7595186298710257905">"com.android.bluetooth.pbapsink"</string>
-</resources>
diff --git a/android/app/res/values/strings_pbap_client.xml b/android/app/res/values/strings_pbap_client.xml
index 465d4ee..e528702 100644
--- a/android/app/res/values/strings_pbap_client.xml
+++ b/android/app/res/values/strings_pbap_client.xml
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="pbap_account_type">com.android.bluetooth.pbapsink</string>
+    <string name="pbap_client_account_type" translatable="false">com.android.bluetooth.pbapclient</string>
 </resources>
diff --git a/android/app/res/xml/authenticator.xml b/android/app/res/xml/authenticator.xml
index ab08a61..e7a1d62 100644
--- a/android/app/res/xml/authenticator.xml
+++ b/android/app/res/xml/authenticator.xml
@@ -17,4 +17,4 @@
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:icon="@mipmap/bt_share"
         android:smallIcon="@mipmap/bt_share"
-        android:accountType="@string/pbap_account_type" />
+        android:accountType="@string/pbap_client_account_type" />
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index 82af2ac..9d744db 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothSinkAudioPolicy;
 import android.media.AudioDeviceCallback;
@@ -569,8 +570,8 @@
             boolean hasFallbackDevice = false;
             if (Objects.equals(mLeAudioActiveDevice, device)) {
                 hasFallbackDevice = setFallbackDeviceActiveLocked(device);
-                if (!hasFallbackDevice) {
-                    setLeAudioActiveDevice(null, false);
+                if (!hasFallbackDevice && !Flags.admFixDisconnectOfSetMember()) {
+                    leAudioService.removeActiveDevice(false);
                 }
             }
             leAudioService.deviceDisconnected(device, hasFallbackDevice);
@@ -1095,13 +1096,25 @@
             return false;
         }
 
+        if (firstDevice == null || secondDevice == null) {
+            return false;
+        }
+
         final LeAudioService leAudioService = mFactory.getLeAudioService();
         if (leAudioService == null) {
             Log.e(TAG, "LeAudioService not available");
             return false;
         }
 
-        return leAudioService.getGroupId(firstDevice) == leAudioService.getGroupId(secondDevice);
+        int groupIdFirst = leAudioService.getGroupId(firstDevice);
+        int groupIdSecond = leAudioService.getGroupId(secondDevice);
+
+        if (groupIdFirst == BluetoothLeAudio.GROUP_ID_INVALID
+                || groupIdSecond == BluetoothLeAudio.GROUP_ID_INVALID) {
+            return false;
+        }
+
+        return groupIdFirst == groupIdSecond;
     }
 
     /**
@@ -1113,7 +1126,7 @@
      */
     @GuardedBy("mLock")
     private boolean setFallbackDeviceActiveLocked(BluetoothDevice recentlyRemovedDevice) {
-        Log.d(TAG, "setFallbackDeviceActive");
+        Log.d(TAG, "setFallbackDeviceActive, recently removed: " + recentlyRemovedDevice);
         mDbManager = mAdapterService.getDatabase();
         List<BluetoothDevice> connectedHearingAidDevices = new ArrayList<>();
         if (!mHearingAidConnectedDevices.isEmpty()) {
@@ -1368,7 +1381,18 @@
      */
     private boolean isBroadcastingAudio() {
         final LeAudioService leAudioService = mFactory.getLeAudioService();
-        return leAudioService != null && !leAudioService.getAllBroadcastMetadata().isEmpty();
+        if (leAudioService == null) {
+            Log.d(TAG, "isBroadcastingAudio: false - there is no LeAudioService");
+            return false;
+        }
+
+        if (leAudioService.getAllBroadcastMetadata().isEmpty()) {
+            Log.d(TAG, "isBroadcastingAudio: false - getAllBroadcastMetadata is empty");
+            return false;
+        }
+
+        Log.d(TAG, "isBroadcastingAudio: true");
+        return true;
     }
 
     /**
diff --git a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
index 6a20a7f..63bb766 100644
--- a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
+++ b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
@@ -1020,6 +1020,7 @@
         BatteryService batteryService = mFactory.getBatteryService();
         HidHostService hidHostService = mFactory.getHidHostService();
         BassClientService bcService = mFactory.getBassClientService();
+        HapClientService hapClientService = mFactory.getHapClientService();
 
         if (hsService != null) {
             if (!mHeadsetRetrySet.contains(device)
@@ -1120,6 +1121,19 @@
                 bcService.connect(device);
             }
         }
+        if (Flags.connectHapOnOtherProfileConnect()) {
+            if (hapClientService != null) {
+                List<BluetoothDevice> connectedDevices = hapClientService.getConnectedDevices();
+                if (!connectedDevices.contains(device)
+                        && (hapClientService.getConnectionPolicy(device)
+                                == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
+                        && (hapClientService.getConnectionState(device)
+                                == BluetoothProfile.STATE_DISCONNECTED)) {
+                    debugLog("Retrying connection to HAS with device " + device);
+                    hapClientService.connect(device);
+                }
+            }
+        }
     }
 
     /**
diff --git a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java
index 850fb72..a23af3a 100644
--- a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java
@@ -32,6 +32,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -202,6 +203,11 @@
         return ChannelSoundingParams.CS_SECURITY_LEVEL_ONE;
     }
 
+    Set<Integer> getChannelSoundingSupportedSecurityLevels() {
+        // TODO(b/378685103): get it from the HAL when level 4 is supported and HAL v2 is available.
+        return Set.of(ChannelSoundingParams.CS_SECURITY_LEVEL_ONE);
+    }
+
     private synchronized int stopRssiTracker(UUID uuid, String identityAddress, boolean timeout) {
         CopyOnWriteArraySet<DistanceMeasurementTracker> set = mRssiTrackers.get(identityAddress);
         if (set == null) {
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index 9c0daa0..2be39b6 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -241,9 +241,7 @@
         }
         mAdvertiseManager.clear();
         mClientMap.clear();
-        if (Flags.gattCleanupRestrictedHandles()) {
-            mRestrictedHandles.clear();
-        }
+        mRestrictedHandles.clear();
         mServerMap.clear();
         mHandleMap.clear();
         mReliableQueue.clear();
@@ -1266,6 +1264,26 @@
             service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
             return service.getLocalChannelSoundingMaxSupportedSecurityLevel();
         }
+
+        @Override
+        public int[] getChannelSoundingSupportedSecurityLevels(
+                AttributionSource attributionSource) {
+            GattService service = getService();
+
+            if (service == null
+                    || !callerIsSystemOrActiveOrManagedUser(
+                            service, TAG, "GattService getChannelSoundingSupportedSecurityLevels")
+                    || !Utils.checkConnectPermissionForDataDelivery(
+                            service,
+                            attributionSource,
+                            "GattService getChannelSoundingSupportedSecurityLevels")) {
+                return new int[0];
+            }
+            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+            return service.getChannelSoundingSupportedSecurityLevels().stream()
+                    .mapToInt(i -> i)
+                    .toArray();
+        }
     }
     ;
 
@@ -1335,9 +1353,7 @@
         mClientMap.removeConnection(clientIf, connId);
         ContextMap<IBluetoothGattCallback>.App app = mClientMap.getById(clientIf);
 
-        if (Flags.gattCleanupRestrictedHandles()) {
-            mRestrictedHandles.remove(connId);
-        }
+        mRestrictedHandles.remove(connId);
 
         // Remove AtomicBoolean representing permit if no other connections rely on this remote
         // device.
@@ -2097,6 +2113,10 @@
         return mDistanceMeasurementManager.getLocalChannelSoundingMaxSupportedSecurityLevel();
     }
 
+    Set<Integer> getChannelSoundingSupportedSecurityLevels() {
+        return mDistanceMeasurementManager.getChannelSoundingSupportedSecurityLevels();
+    }
+
     /**************************************************************************
      * GATT Service functions - CLIENT
      *************************************************************************/
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index facb63f..09f2b63 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -280,7 +280,10 @@
         }
     }
 
-    List<BluetoothDevice> getConnectedDevices() {
+    /**
+     * @return A list of connected {@link BluetoothDevice}.
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
         synchronized (mStateMachines) {
             List<BluetoothDevice> devices = new ArrayList<>();
             for (HapClientStateMachine sm : mStateMachines.values()) {
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index 9897e69..32dbf73 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -217,7 +217,6 @@
         return amVol;
     }
 
-    @VisibleForTesting
     int amToHfVol(int amVol) {
         int amRange = (mMaxAmVcVol > mMinAmVcVol) ? (mMaxAmVcVol - mMinAmVcVol) : 1;
         int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
index ee53a4d..fa01088 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@@ -14,11 +14,21 @@
  * limitations under the License.
  */
 
-/**
- * Bluetooth Headset Client StateMachine (Disconnected) | ^ ^ CONNECT | | | DISCONNECTED V | |
- * (Connecting) | | | CONNECTED | | DISCONNECT V | (Connected) | ^ CONNECT_AUDIO | |
- * DISCONNECT_AUDIO V | (AudioOn)
- */
+// Bluetooth Headset Client State Machine
+//                (Disconnected)
+//                  |        ^
+//          CONNECT |        | DISCONNECTED
+//                  V        |
+//          (Connecting)   (Disconnecting)
+//                  |        ^
+//        CONNECTED |        | DISCONNECT
+//                  V        |
+//                  (Connected)
+//                  |        |
+//    CONNECT_AUDIO |        | DISCONNECT_AUDIO
+//                  |        |
+//                  (Audio_On)
+
 package com.android.bluetooth.hfpclient;
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
@@ -56,6 +66,7 @@
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.MetricsLogger;
 import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.flags.Flags;
 import com.android.bluetooth.hfp.HeadsetService;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IState;
@@ -109,12 +120,14 @@
     @VisibleForTesting static final int QUERY_OPERATOR_NAME = 51;
     @VisibleForTesting static final int SUBSCRIBER_INFO = 52;
     @VisibleForTesting static final int CONNECTING_TIMEOUT = 53;
+    @VisibleForTesting static final int DISCONNECTING_TIMEOUT = 54;
 
     // special action to handle terminating specific call from multiparty call
     static final int TERMINATE_SPECIFIC_CALL = 53;
 
     // Timeouts.
     @VisibleForTesting static final int CONNECTING_TIMEOUT_MS = 10000; // 10s
+    @VisibleForTesting static final int DISCONNECTING_TIMEOUT_MS = 10000; // 10s
     private static final int ROUTING_DELAY_MS = 250;
 
     static final int HF_ORIGINATED_CALL_ID = -1;
@@ -127,6 +140,7 @@
     private final Disconnected mDisconnected;
     private final Connecting mConnecting;
     private final Connected mConnected;
+    private final Disconnecting mDisconnecting;
     private final AudioOn mAudioOn;
     private State mPrevState;
 
@@ -314,6 +328,8 @@
                 return "SUBSCRIBER_INFO";
             case CONNECTING_TIMEOUT:
                 return "CONNECTING_TIMEOUT";
+            case DISCONNECTING_TIMEOUT:
+                return "DISCONNECTING_TIMEOUT";
             default:
                 return "UNKNOWN(" + what + ")";
         }
@@ -932,11 +948,15 @@
         mConnecting = new Connecting();
         mConnected = new Connected();
         mAudioOn = new AudioOn();
+        mDisconnecting = new Disconnecting();
 
         addState(mDisconnected);
         addState(mConnecting);
         addState(mConnected);
         addState(mAudioOn, mConnected);
+        if (Flags.hfpClientDisconnectingState()) {
+            addState(mDisconnecting);
+        }
 
         setInitialState(mDisconnected);
     }
@@ -1003,7 +1023,11 @@
     class Disconnected extends State {
         @Override
         public void enter() {
-            debug("Enter Disconnected: " + getCurrentMessage().what);
+            debug(
+                    "Enter Disconnected: from state="
+                            + mPrevState
+                            + ", message="
+                            + getMessageName(getCurrentMessage().what));
 
             // cleanup
             mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
@@ -1040,7 +1064,15 @@
                         mCurrentDevice,
                         BluetoothProfile.STATE_DISCONNECTED,
                         BluetoothProfile.STATE_CONNECTED);
-            } else if (mPrevState != null) { // null is the default state before Disconnected
+            } else if (Flags.hfpClientDisconnectingState()) {
+                if (mPrevState == mDisconnecting) {
+                    broadcastConnectionState(
+                            mCurrentDevice,
+                            BluetoothProfile.STATE_DISCONNECTED,
+                            BluetoothProfile.STATE_DISCONNECTING);
+                }
+            } else if (mPrevState != null) {
+                // null is the default state before Disconnected
                 error(
                         "Disconnected: Illegal state transition from "
                                 + mPrevState.getName()
@@ -1139,7 +1171,7 @@
 
         @Override
         public void exit() {
-            debug("Exit Disconnected: " + getCurrentMessage().what);
+            debug("Exit Disconnected: " + getMessageName(getCurrentMessage().what));
             mPrevState = this;
         }
     }
@@ -1147,7 +1179,7 @@
     class Connecting extends State {
         @Override
         public void enter() {
-            debug("Enter Connecting: " + getCurrentMessage().what);
+            debug("Enter Connecting: " + getMessageName(getCurrentMessage().what));
             // This message is either consumed in processMessage or
             // removed in exit. It is safe to send a CONNECTING_TIMEOUT here since
             // the only transition is when connection attempt is initiated.
@@ -1351,7 +1383,7 @@
 
         @Override
         public void exit() {
-            debug("Exit Connecting: " + getCurrentMessage().what);
+            debug("Exit Connecting: " + getMessageName(getCurrentMessage().what));
             removeMessages(CONNECTING_TIMEOUT);
             mPrevState = this;
         }
@@ -1362,7 +1394,7 @@
 
         @Override
         public void enter() {
-            debug("Enter Connected: " + getCurrentMessage().what);
+            debug("Enter Connected: " + getMessageName(getCurrentMessage().what));
             mAudioWbs = false;
             mAudioSWB = false;
             mCommandedSpeakerVolume = -1;
@@ -1409,7 +1441,12 @@
                     if (!mCurrentDevice.equals(dev)) {
                         break;
                     }
-                    if (!mNativeInterface.disconnect(dev)) {
+                    if (Flags.hfpClientDisconnectingState()) {
+                        if (!mNativeInterface.disconnect(mCurrentDevice)) {
+                            warn("disconnectNative failed for " + mCurrentDevice);
+                        }
+                        transitionTo(mDisconnecting);
+                    } else if (!mNativeInterface.disconnect(dev)) {
                         error("disconnectNative failed for " + dev);
                     }
                     break;
@@ -1569,7 +1606,7 @@
                                             + event.device
                                             + ": "
                                             + event.valueInt);
-                            processConnectionEvent(event.valueInt, event.device);
+                            processConnectionEvent(message, event.valueInt, event.device);
                             break;
                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
                             debug(
@@ -1810,13 +1847,19 @@
         }
 
         // in Connected state
-        private void processConnectionEvent(int state, BluetoothDevice device) {
+        private void processConnectionEvent(Message message, int state, BluetoothDevice device) {
             switch (state) {
                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
                     debug("Connected disconnects.");
                     // AG disconnects
                     if (mCurrentDevice.equals(device)) {
-                        transitionTo(mDisconnected);
+                        if (Flags.hfpClientDisconnectingState()) {
+                            transitionTo(mDisconnecting);
+                            // message is deferred to be processed in the disconnecting state
+                            deferMessage(message);
+                        } else {
+                            transitionTo(mDisconnected);
+                        }
                     } else {
                         error("Disconnected from unknown device: " + device);
                     }
@@ -1915,7 +1958,101 @@
 
         @Override
         public void exit() {
-            debug("Exit Connected: " + getCurrentMessage().what);
+            debug("Exit Connected: " + getMessageName(getCurrentMessage().what));
+            mPrevState = this;
+        }
+    }
+
+    class Disconnecting extends State {
+        @Override
+        public void enter() {
+            debug(
+                    "Disconnecting: enter disconnecting from state="
+                            + mPrevState
+                            + ", message="
+                            + getMessageName(getCurrentMessage().what));
+            if (mPrevState == mConnected || mPrevState == mAudioOn) {
+                broadcastConnectionState(
+                        mCurrentDevice,
+                        BluetoothProfile.STATE_DISCONNECTING,
+                        BluetoothProfile.STATE_CONNECTED);
+            } else {
+                String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
+                error(
+                        "Disconnecting: Illegal state transition from "
+                                + prevStateName
+                                + " to Disconnecting");
+            }
+            sendMessageDelayed(DISCONNECTING_TIMEOUT, DISCONNECTING_TIMEOUT_MS);
+        }
+
+        @Override
+        public synchronized boolean processMessage(Message message) {
+            debug("Disconnecting: Process message: " + message.what);
+
+            switch (message.what) {
+                    // Defering messages as state machine objects are meant to be reused and after
+                    // disconnect is complete we want honor other message requests
+                case CONNECT:
+                case CONNECT_AUDIO:
+                case DISCONNECT:
+                case DISCONNECT_AUDIO:
+                    deferMessage(message);
+                    break;
+
+                case DISCONNECTING_TIMEOUT:
+                    // We timed out trying to disconnect, force transition to disconnected.
+                    warn("Disconnecting: Disconnection timeout for " + mCurrentDevice);
+                    transitionTo(mDisconnected);
+                    break;
+
+                case StackEvent.STACK_EVENT:
+                    StackEvent event = (StackEvent) message.obj;
+
+                    switch (event.type) {
+                        case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                            debug(
+                                    "Disconnecting: Connection state changed: "
+                                            + event.device
+                                            + ": "
+                                            + event.valueInt);
+                            processConnectionEvent(event.valueInt, event.device);
+                            break;
+                        default:
+                            error("Disconnecting: Unknown stack event: " + event.type);
+                            break;
+                    }
+                    break;
+                default:
+                    warn("Disconnecting: Message not handled " + message);
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        private void processConnectionEvent(int state, BluetoothDevice device) {
+            switch (state) {
+                case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
+                    if (mCurrentDevice.equals(device)) {
+                        transitionTo(mDisconnected);
+                    } else {
+                        error("Disconnecting: Disconnected from unknown device: " + device);
+                    }
+                    break;
+                default:
+                    error(
+                            "Disconnecting: Connection State Device: "
+                                    + device
+                                    + " bad state: "
+                                    + state);
+                    break;
+            }
+        }
+
+        @Override
+        public void exit() {
+            debug("Disconnecting: Exit Disconnecting: " + getMessageName(getCurrentMessage().what));
+            removeMessages(DISCONNECTING_TIMEOUT);
             mPrevState = this;
         }
     }
@@ -1923,7 +2060,7 @@
     class AudioOn extends State {
         @Override
         public void enter() {
-            debug("Enter AudioOn: " + getCurrentMessage().what);
+            debug("Enter AudioOn: " + getMessageName(getCurrentMessage().what));
             broadcastAudioState(
                     mCurrentDevice,
                     BluetoothHeadsetClient.STATE_AUDIO_CONNECTED,
@@ -1975,7 +2112,7 @@
                                             + event.device
                                             + ": "
                                             + event.valueInt);
-                            processConnectionEvent(event.valueInt, event.device);
+                            processConnectionEvent(message, event.valueInt, event.device);
                             break;
                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
                             debug(
@@ -1996,13 +2133,20 @@
         }
 
         // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this
-        private void processConnectionEvent(int state, BluetoothDevice device) {
+        private void processConnectionEvent(Message message, int state, BluetoothDevice device) {
             switch (state) {
                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
                     if (mCurrentDevice.equals(device)) {
                         processAudioEvent(
                                 HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED, device);
-                        transitionTo(mDisconnected);
+                        if (Flags.hfpClientDisconnectingState()) {
+                            transitionTo(mDisconnecting);
+                            // message is deferred to be processed in the disconnecting state
+                            deferMessage(message);
+                        } else {
+                            transitionTo(mDisconnected);
+                        }
+
                     } else {
                         error("Disconnected from unknown device: " + device);
                     }
@@ -2041,7 +2185,7 @@
 
         @Override
         public void exit() {
-            debug("Exit AudioOn: " + getCurrentMessage().what);
+            debug("Exit AudioOn: " + getMessageName(getCurrentMessage().what));
             mPrevState = this;
             broadcastAudioState(
                     mCurrentDevice,
@@ -2064,7 +2208,11 @@
             return BluetoothProfile.STATE_CONNECTED;
         }
 
-        error("Bad currentState: " + currentState);
+        if (Flags.hfpClientDisconnectingState()) {
+            if (currentState == mDisconnecting) {
+                return BluetoothProfile.STATE_DISCONNECTING;
+            }
+        }
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
index 2e0a80c..8bf6e41 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -24,8 +24,8 @@
 import static com.android.bluetooth.bass_client.BassConstants.INVALID_BROADCAST_ID;
 import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask;
 import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState;
-import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
 import static com.android.bluetooth.flags.Flags.leaudioBroadcastApiManagePrimaryGroup;
+import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
 import static com.android.bluetooth.flags.Flags.leaudioUseAudioModeListener;
 import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
 
@@ -843,6 +843,35 @@
         mNativeInterface.setEnableState(device, enabled);
     }
 
+    private void setDefaultBroadcastToUnicastFallbackGroup() {
+        DatabaseManager dbManager = mAdapterService.getDatabase();
+        if (dbManager == null) {
+            Log.i(
+                    TAG,
+                    "Can't get db manager to pick default Broadcast to Unicast fallback group"
+                            + ", leaving: "
+                            + mUnicastGroupIdDeactivatedForBroadcastTransition);
+            return;
+        }
+
+        List<BluetoothDevice> devices = dbManager.getMostRecentlyConnectedDevices();
+
+        int targetDeviceIdx = -1;
+        int targetGroupId = LE_AUDIO_GROUP_ID_INVALID;
+        for (BluetoothDevice device : getConnectedDevices()) {
+            LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device);
+            if (devices.contains(device)) {
+                int idx = devices.indexOf(device);
+                if (idx > targetDeviceIdx) {
+                    targetDeviceIdx = idx;
+                    targetGroupId = descriptor.mGroupId;
+                }
+            }
+        }
+
+        updateFallbackUnicastGroupIdForBroadcast(targetGroupId);
+    }
+
     public boolean connect(BluetoothDevice device) {
         Log.d(TAG, "connect(): " + device);
 
@@ -2417,7 +2446,8 @@
                         + ", mExposedActiveDevice: "
                         + mExposedActiveDevice);
 
-        if (isBroadcastActive()
+        if (!Flags.leaudioBroadcastPrimaryGroupSelection()
+                && isBroadcastActive()
                 && currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID
                 && mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) {
 
@@ -3205,7 +3235,9 @@
                     TAG,
                     "transitionFromBroadcastToUnicast: No valid unicast device for group ID: "
                             + mUnicastGroupIdDeactivatedForBroadcastTransition);
-            updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
+            if (!Flags.leaudioBroadcastPrimaryGroupSelection()) {
+                updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
+            }
             updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false);
             return;
         }
@@ -3217,7 +3249,9 @@
                         + ", with device: "
                         + unicastDevice);
 
-        updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
+        if (!Flags.leaudioBroadcastPrimaryGroupSelection()) {
+            updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
+        }
         setActiveDevice(unicastDevice);
     }
 
@@ -3705,7 +3739,9 @@
                         if (isBroadcastAllowedToBeActivateInCurrentAudioMode()) {
                             /* Check if broadcast was deactivated due to unicast */
                             if (mBroadcastIdDeactivatedForUnicastTransition.isPresent()) {
-                                updateFallbackUnicastGroupIdForBroadcast(groupId);
+                                if (!Flags.leaudioBroadcastPrimaryGroupSelection()) {
+                                    updateFallbackUnicastGroupIdForBroadcast(groupId);
+                                }
                                 if (!leaudioUseAudioModeListener()) {
                                     mQueuedInCallValue = Optional.empty();
                                 }
@@ -3722,7 +3758,9 @@
                                 }
                             } else {
                                 if (!mCreateBroadcastQueue.isEmpty()) {
-                                    updateFallbackUnicastGroupIdForBroadcast(groupId);
+                                    if (!Flags.leaudioBroadcastPrimaryGroupSelection()) {
+                                        updateFallbackUnicastGroupIdForBroadcast(groupId);
+                                    }
                                     BluetoothLeBroadcastSettings settings =
                                             mCreateBroadcastQueue.remove();
                                     createBroadcast(settings);
@@ -4181,6 +4219,12 @@
         if (!isScannerNeeded()) {
             stopAudioServersBackgroundScan();
         }
+
+        /* Set by default earliest connected device */
+        if (Flags.leaudioBroadcastPrimaryGroupSelection()
+                && mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) {
+            setDefaultBroadcastToUnicastFallbackGroup();
+        }
     }
 
     /** Process a change for disconnection of a device. */
@@ -4246,6 +4290,12 @@
                             false);
                     return;
                 }
+
+                /* Set by default earliest connected device */
+                if (Flags.leaudioBroadcastPrimaryGroupSelection()
+                        && mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) {
+                    setDefaultBroadcastToUnicastFallbackGroup();
+                }
             }
 
             if (descriptor.isActive()
@@ -4840,6 +4890,12 @@
             mGroupWriteLock.unlock();
         }
 
+        /* Set by default earliest connected device */
+        if (Flags.leaudioBroadcastPrimaryGroupSelection()
+                && mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) {
+            setDefaultBroadcastToUnicastFallbackGroup();
+        }
+
         if (mBluetoothEnabled) {
             setAuthorizationForRelatedProfiles(device, true);
             startAudioServersBackgroundScan(/* retry= */ false);
@@ -4905,7 +4961,11 @@
                 }
 
                 if (mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) {
-                    updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
+                    if (Flags.leaudioBroadcastPrimaryGroupSelection()) {
+                        setDefaultBroadcastToUnicastFallbackGroup();
+                    } else {
+                        updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
+                    }
                 }
             }
             mHandler.post(() -> notifyGroupNodeRemoved(device, groupId));
@@ -5231,6 +5291,93 @@
         mNativeInterface.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig);
     }
 
+    void setBroadcastToUnicastFallbackGroup(int groupId) {
+        if (!leaudioBroadcastApiManagePrimaryGroup()) {
+            return;
+        }
+
+        Log.d(TAG, "setBroadcastToUnicastFallbackGroup(" + groupId + ")");
+
+        if (mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) {
+            Log.d(TAG, "Requested Broadcast to Unicast fallback group is already set");
+            return;
+        }
+
+        mGroupReadLock.lock();
+        try {
+            LeAudioGroupDescriptor oldFallbackGroupDescriptor =
+                    getGroupDescriptor(mUnicastGroupIdDeactivatedForBroadcastTransition);
+            LeAudioGroupDescriptor newFallbackGroupDescriptor = getGroupDescriptor(groupId);
+            if (oldFallbackGroupDescriptor == null && newFallbackGroupDescriptor == null) {
+                Log.w(
+                        TAG,
+                        "Failed to set Broadcast to Unicast Fallback group "
+                                + "(lack of new and old group descriptors): "
+                                + mUnicastGroupIdDeactivatedForBroadcastTransition
+                                + " -> "
+                                + groupId);
+                return;
+            }
+
+            /* Fallback group should be not updated if new group is not connected or requested
+             * group ID is different than INVALID but there is no such descriptor.
+             */
+            if (groupId != LE_AUDIO_GROUP_ID_INVALID
+                    && (newFallbackGroupDescriptor == null
+                            || !newFallbackGroupDescriptor.mIsConnected)) {
+                Log.w(
+                        TAG,
+                        "Failed to set Broadcast to Unicast Fallback group (invalid new group): "
+                                + mUnicastGroupIdDeactivatedForBroadcastTransition
+                                + " -> "
+                                + groupId);
+                return;
+            }
+
+            /* Update exposed monitoring input device while being in Broadcast mode */
+            if (isBroadcastActive()
+                    && getActiveGroupId() == LE_AUDIO_GROUP_ID_INVALID
+                    && mUnicastGroupIdDeactivatedForBroadcastTransition
+                            != LE_AUDIO_GROUP_ID_INVALID) {
+                /* In case of removing fallback unicast group, monitoring input
+                 * device should be removed from active devices.
+                 */
+                int newDirection = AUDIO_DIRECTION_NONE;
+                int oldDirection = oldFallbackGroupDescriptor != null
+                        ? oldFallbackGroupDescriptor.mDirection : AUDIO_DIRECTION_NONE;
+                boolean notifyAndUpdateInactiveOutDeviceOnly = false;
+                boolean hasFallbackDeviceWhenGettingInactive = oldFallbackGroupDescriptor != null
+                        ? oldFallbackGroupDescriptor.mHasFallbackDeviceWhenGettingInactive
+                        : false;
+                if (groupId != LE_AUDIO_GROUP_ID_INVALID) {
+                    newDirection = AUDIO_DIRECTION_INPUT_BIT;
+                    notifyAndUpdateInactiveOutDeviceOnly = true;
+                }
+                updateActiveDevices(
+                        groupId,
+                        oldDirection,
+                        newDirection,
+                        false, // isActive
+                        hasFallbackDeviceWhenGettingInactive,
+                        notifyAndUpdateInactiveOutDeviceOnly);
+            }
+        } finally {
+            mGroupReadLock.unlock();
+        }
+
+        updateFallbackUnicastGroupIdForBroadcast(groupId);
+    }
+
+    int getBroadcastToUnicastFallbackGroup() {
+        if (!leaudioBroadcastApiManagePrimaryGroup()) {
+            return LE_AUDIO_GROUP_ID_INVALID;
+        }
+
+        Log.v(TAG, "getBroadcastToUnicastFallbackGroup()");
+
+        return mUnicastGroupIdDeactivatedForBroadcastTransition;
+    }
+
     /**
      * Checks if the remote device supports LE Audio duplex (output and input).
      *
@@ -5908,6 +6055,28 @@
         }
 
         @Override
+        public void setBroadcastToUnicastFallbackGroup(int groupId, AttributionSource source) {
+            LeAudioService service = getServiceAndEnforceConnect(source);
+            if (service == null) {
+                return;
+            }
+
+            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+            service.setBroadcastToUnicastFallbackGroup(groupId);
+        }
+
+        @Override
+        public int getBroadcastToUnicastFallbackGroup(AttributionSource source) {
+            LeAudioService service = getServiceAndEnforceConnect(source);
+            if (service == null) {
+                return LE_AUDIO_GROUP_ID_INVALID;
+            }
+
+            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+            return service.getBroadcastToUnicastFallbackGroup();
+        }
+
+        @Override
         public boolean isBroadcastActive(AttributionSource source) {
             LeAudioService service = getServiceAndEnforceConnect(source);
             if (service == null) {
diff --git a/android/app/src/com/android/bluetooth/pbapclient/Authenticator.java b/android/app/src/com/android/bluetooth/pbapclient/Authenticator.java
deleted file mode 100644
index 899e2e6..0000000
--- a/android/app/src/com/android/bluetooth/pbapclient/Authenticator.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.bluetooth.pbapclient;
-
-import android.accounts.AbstractAccountAuthenticator;
-import android.accounts.Account;
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.AccountManager;
-import android.accounts.NetworkErrorException;
-import android.content.Context;
-import android.os.Bundle;
-import android.util.Log;
-
-public class Authenticator extends AbstractAccountAuthenticator {
-    private static final String TAG = "PbapClientAuthenticator";
-
-    public Authenticator(Context context) {
-        super(context);
-    }
-
-    // Editing properties is not supported
-    @Override
-    public Bundle editProperties(AccountAuthenticatorResponse r, String s) {
-        Log.d(TAG, "got call", new Exception());
-        throw new UnsupportedOperationException();
-    }
-
-    // Don't add additional accounts
-    @Override
-    public Bundle addAccount(
-            AccountAuthenticatorResponse r, String s, String s2, String[] strings, Bundle bundle)
-            throws NetworkErrorException {
-        Log.d(TAG, "got call", new Exception());
-        // Don't allow accounts to be added.
-        throw new UnsupportedOperationException();
-    }
-
-    // Ignore attempts to confirm credentials
-    @Override
-    public Bundle confirmCredentials(AccountAuthenticatorResponse r, Account account, Bundle bundle)
-            throws NetworkErrorException {
-        Log.d(TAG, "got call", new Exception());
-        return null;
-    }
-
-    // Getting an authentication token is not supported
-    @Override
-    public Bundle getAuthToken(
-            AccountAuthenticatorResponse r, Account account, String s, Bundle bundle)
-            throws NetworkErrorException {
-        Log.d(TAG, "got call", new Exception());
-        throw new UnsupportedOperationException();
-    }
-
-    // Getting a label for the auth token is not supported
-    @Override
-    public String getAuthTokenLabel(String s) {
-        Log.d(TAG, "got call", new Exception());
-        return null;
-    }
-
-    // Updating user credentials is not supported
-    @Override
-    public Bundle updateCredentials(
-            AccountAuthenticatorResponse r, Account account, String s, Bundle bundle)
-            throws NetworkErrorException {
-        Log.d(TAG, "got call", new Exception());
-        return null;
-    }
-
-    // Checking features for the account is not supported
-    @Override
-    public Bundle hasFeatures(AccountAuthenticatorResponse r, Account account, String[] strings)
-            throws NetworkErrorException {
-        Log.d(TAG, "got call", new Exception());
-
-        final Bundle result = new Bundle();
-        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
-        return result;
-    }
-}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequest.java b/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequest.java
deleted file mode 100644
index 77e266e..0000000
--- a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import android.util.Log;
-
-import com.android.obex.ClientOperation;
-import com.android.obex.ClientSession;
-import com.android.obex.HeaderSet;
-import com.android.obex.ResponseCodes;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-abstract class BluetoothPbapRequest {
-    static final String TAG = "PbapClient.BaseRequest";
-
-    protected static final byte OAP_TAGID_ORDER = 0x01;
-    protected static final byte OAP_TAGID_SEARCH_VALUE = 0x02;
-    protected static final byte OAP_TAGID_SEARCH_ATTRIBUTE = 0x03;
-    protected static final byte OAP_TAGID_MAX_LIST_COUNT = 0x04;
-    protected static final byte OAP_TAGID_LIST_START_OFFSET = 0x05;
-    protected static final byte OAP_TAGID_FILTER = 0x06;
-    protected static final byte OAP_TAGID_FORMAT = 0x07;
-    protected static final byte OAP_TAGID_PHONEBOOK_SIZE = 0x08;
-    protected static final byte OAP_TAGID_NEW_MISSED_CALLS = 0x09;
-    protected static final byte OAP_TAGID_PBAP_SUPPORTED_FEATURES = 0x10;
-
-    protected HeaderSet mHeaderSet;
-
-    protected int mResponseCode;
-
-    private boolean mAborted = false;
-
-    private ClientOperation mOp = null;
-
-    BluetoothPbapRequest() {
-        mHeaderSet = new HeaderSet();
-    }
-
-    public final boolean isSuccess() {
-        return (mResponseCode == ResponseCodes.OBEX_HTTP_OK);
-    }
-
-    public void execute(ClientSession session) throws IOException {
-        Log.v(TAG, "execute");
-
-        /* in case request is aborted before can be executed */
-        if (mAborted) {
-            mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
-            return;
-        }
-
-        try {
-            mOp = (ClientOperation) session.get(mHeaderSet);
-
-            /* make sure final flag for GET is used (PBAP spec 6.2.2) */
-            mOp.setGetFinalFlag(true);
-
-            /*
-             * this will trigger ClientOperation to use non-buffered stream so
-             * we can abort operation
-             */
-            mOp.continueOperation(true, false);
-
-            readResponseHeaders(mOp.getReceivedHeader());
-
-            InputStream is = mOp.openInputStream();
-            readResponse(is);
-            is.close();
-
-            mOp.close();
-
-            mResponseCode = mOp.getResponseCode();
-
-            Log.d(TAG, "mResponseCode=" + mResponseCode);
-
-            checkResponseCode(mResponseCode);
-        } catch (IOException e) {
-            Log.e(TAG, "IOException occurred when processing request", e);
-            mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
-
-            throw e;
-        }
-    }
-
-    public void abort() {
-        mAborted = true;
-
-        if (mOp != null) {
-            try {
-                mOp.abort();
-            } catch (IOException e) {
-                Log.e(TAG, "Exception occurred when trying to abort", e);
-            }
-        }
-    }
-
-    protected void readResponse(InputStream stream) throws IOException {
-        Log.v(TAG, "readResponse");
-
-        /* nothing here by default */
-    }
-
-    protected void readResponseHeaders(HeaderSet headerset) {
-        Log.v(TAG, "readResponseHeaders");
-
-        /* nothing here by default */
-    }
-
-    protected void checkResponseCode(int responseCode) throws IOException {
-        Log.v(TAG, "checkResponseCode");
-
-        /* nothing here by default */
-    }
-}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java b/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java
deleted file mode 100644
index becb7e6..0000000
--- a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import android.accounts.Account;
-import android.util.Log;
-
-import com.android.bluetooth.ObexAppParameters;
-import com.android.obex.HeaderSet;
-import com.android.vcard.VCardEntry;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-
-final class BluetoothPbapRequestPullPhoneBook extends BluetoothPbapRequest {
-    private static final String TAG = "PbapClient.PullPb";
-
-    private static final String TYPE = "x-bt/phonebook";
-
-    private BluetoothPbapVcardList mResponse;
-
-    private Account mAccount;
-
-    private int mNewMissedCalls = -1;
-
-    private final byte mFormat;
-
-    BluetoothPbapRequestPullPhoneBook(
-            String pbName,
-            Account account,
-            long filter,
-            byte format,
-            int maxListCount,
-            int listStartOffset) {
-        mAccount = account;
-        if (maxListCount < 0 || maxListCount > 65535) {
-            throw new IllegalArgumentException("maxListCount should be [0..65535]");
-        }
-
-        if (listStartOffset < 0 || listStartOffset > 65535) {
-            throw new IllegalArgumentException("listStartOffset should be [0..65535]");
-        }
-
-        mHeaderSet.setHeader(HeaderSet.NAME, pbName);
-
-        mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
-
-        ObexAppParameters oap = new ObexAppParameters();
-
-        /* make sure format is one of allowed values */
-        if (format != PbapClientConnectionHandler.VCARD_TYPE_21
-                && format != PbapClientConnectionHandler.VCARD_TYPE_30) {
-            format = PbapClientConnectionHandler.VCARD_TYPE_21;
-        }
-
-        if (filter != 0) {
-            oap.add(OAP_TAGID_FILTER, filter);
-        }
-
-        oap.add(OAP_TAGID_FORMAT, format);
-
-        /*
-         * maxListCount == 0 is a special case which is handled in
-         * BluetoothPbapRequestPullPhoneBookSize
-         */
-        if (maxListCount > 0) {
-            oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) maxListCount);
-        } else {
-            oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 65535);
-        }
-
-        if (listStartOffset > 0) {
-            oap.add(OAP_TAGID_LIST_START_OFFSET, (short) listStartOffset);
-        }
-
-        oap.addToHeaderSet(mHeaderSet);
-
-        mFormat = format;
-    }
-
-    @Override
-    protected void readResponse(InputStream stream) throws IOException {
-        Log.v(TAG, "readResponse");
-
-        mResponse = new BluetoothPbapVcardList(mAccount, stream, mFormat);
-        Log.d(TAG, "Read " + mResponse.getCount() + " entries");
-    }
-
-    @Override
-    protected void readResponseHeaders(HeaderSet headerset) {
-        Log.v(TAG, "readResponseHeaders");
-
-        ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
-
-        if (oap.exists(OAP_TAGID_NEW_MISSED_CALLS)) {
-            mNewMissedCalls = oap.getByte(OAP_TAGID_NEW_MISSED_CALLS);
-        }
-    }
-
-    public List<VCardEntry> getList() {
-        return mResponse.getList();
-    }
-
-    public int getNewMissedCalls() {
-        return mNewMissedCalls;
-    }
-}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java b/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java
deleted file mode 100644
index 9432881..0000000
--- a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import android.util.Log;
-
-import com.android.bluetooth.ObexAppParameters;
-import com.android.obex.HeaderSet;
-
-final class BluetoothPbapRequestPullPhoneBookSize extends BluetoothPbapRequest {
-    private static final String TAG = "PbapClient.PullPbSize";
-
-    private static final String TYPE = "x-bt/phonebook";
-
-    private int mSize;
-
-    BluetoothPbapRequestPullPhoneBookSize(String pbName, long filter) {
-        mHeaderSet.setHeader(HeaderSet.NAME, pbName);
-
-        mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
-
-        ObexAppParameters oap = new ObexAppParameters();
-        // Set MaxListCount in the request to 0 to get PhonebookSize in the response.
-        // If a vCardSelector is present in the request, then the result shall
-        // contain the number of items that satisfy the selector’s criteria.
-        // See PBAP v1.2.3, Sec. 5.1.4.5.
-        oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 0);
-        if (filter != 0) {
-            oap.add(OAP_TAGID_FILTER, filter);
-        }
-        oap.addToHeaderSet(mHeaderSet);
-    }
-
-    @Override
-    protected void readResponseHeaders(HeaderSet headerset) {
-        Log.v(TAG, "readResponseHeaders");
-
-        ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
-
-        if (oap.exists(OAP_TAGID_PHONEBOOK_SIZE)) {
-            mSize = oap.getShort(OAP_TAGID_PHONEBOOK_SIZE);
-        }
-    }
-
-    public int getSize() {
-        return mSize;
-    }
-}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardList.java b/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardList.java
deleted file mode 100644
index e2b703a..0000000
--- a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardList.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import android.accounts.Account;
-import android.util.Log;
-
-import com.android.vcard.VCardConfig;
-import com.android.vcard.VCardEntry;
-import com.android.vcard.VCardEntryConstructor;
-import com.android.vcard.VCardEntryHandler;
-import com.android.vcard.VCardParser;
-import com.android.vcard.VCardParser_V21;
-import com.android.vcard.VCardParser_V30;
-import com.android.vcard.exception.VCardException;
-import com.android.vcard.exception.VCardVersionException;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-class BluetoothPbapVcardList {
-    private static final String TAG = BluetoothPbapVcardList.class.getSimpleName();
-    // {@link BufferedInputStream#DEFAULT_BUFFER_SIZE} is not public
-    private static final int BIS_DEFAULT_BUFFER_SIZE = 8192;
-
-    private final List<VCardEntry> mCards = new ArrayList<VCardEntry>();
-    private final Account mAccount;
-
-    class CardEntryHandler implements VCardEntryHandler {
-        @Override
-        public void onStart() {}
-
-        @Override
-        public void onEntryCreated(VCardEntry entry) {
-            mCards.add(entry);
-        }
-
-        @Override
-        public void onEnd() {}
-    }
-
-    BluetoothPbapVcardList(Account account, InputStream in, byte format) throws IOException {
-        if (format != PbapClientConnectionHandler.VCARD_TYPE_21
-                && format != PbapClientConnectionHandler.VCARD_TYPE_30) {
-            throw new IllegalArgumentException("Unsupported vCard version.");
-        }
-        mAccount = account;
-        parse(in, format);
-    }
-
-    private void parse(InputStream in, byte format) throws IOException {
-        VCardParser parser;
-
-        if (format == PbapClientConnectionHandler.VCARD_TYPE_30) {
-            parser = new VCardParser_V30();
-        } else {
-            parser = new VCardParser_V21();
-        }
-
-        VCardEntryConstructor constructor =
-                new VCardEntryConstructor(VCardConfig.VCARD_TYPE_V21_GENERIC, mAccount);
-
-        CardEntryHandler handler = new CardEntryHandler();
-        constructor.addEntryHandler(handler);
-
-        parser.addInterpreter(constructor);
-
-        // {@link BufferedInputStream} supports the {@link InputStream#mark} and
-        // {@link InputStream#reset} methods.
-        BufferedInputStream bufferedInput = new BufferedInputStream(in);
-        bufferedInput.mark(BIS_DEFAULT_BUFFER_SIZE /* readlimit */);
-
-        // If there is a {@link VCardVersionException}, try parsing again with a different
-        // version. Otherwise, parsing either succeeds (i.e., no {@link VCardException}) or it
-        // fails with a different {@link VCardException}.
-        if (parsedWithVcardVersionException(parser, bufferedInput)) {
-            // PBAP v1.2.3 only supports vCard versions 2.1 and 3.0; it's one or the other
-            if (format == PbapClientConnectionHandler.VCARD_TYPE_21) {
-                parser = new VCardParser_V30();
-                Log.w(TAG, "vCard version and Parser mismatch; expected v2.1, switching to v3.0");
-            } else {
-                parser = new VCardParser_V21();
-                Log.w(TAG, "vCard version and Parser mismatch; expected v3.0, switching to v2.1");
-            }
-            // reset and try again
-            bufferedInput.reset();
-            mCards.clear();
-            constructor.clear();
-            parser.addInterpreter(constructor);
-            if (parsedWithVcardVersionException(parser, bufferedInput)) {
-                Log.e(TAG, "unsupported vCard version, neither v2.1 nor v3.0");
-            }
-        }
-    }
-
-    /**
-     * Attempts to parse, with an eye on whether the correct version of Parser is used.
-     *
-     * @param parser -- the {@link VCardParser} to use.
-     * @param in -- the {@link InputStream} to parse.
-     * @return {@code true} if there was a {@link VCardVersionException}; {@code false} if there is
-     *     any other {@link VCardException} or succeeds (i.e., no {@link VCardException}).
-     * @throws IOException if there's an issue reading the {@link InputStream}.
-     */
-    private boolean parsedWithVcardVersionException(VCardParser parser, InputStream in)
-            throws IOException {
-        try {
-            parser.parse(in);
-        } catch (VCardVersionException e1) {
-            Log.w(TAG, "vCard version and Parser mismatch", e1);
-            return true;
-        } catch (VCardException e2) {
-            Log.e(TAG, "vCard exception", e2);
-        }
-        return false;
-    }
-
-    public int getCount() {
-        return mCards.size();
-    }
-
-    public List<VCardEntry> getList() {
-        return mCards;
-    }
-
-    public VCardEntry getFirst() {
-        return mCards.get(0);
-    }
-}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticator.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticator.java
new file mode 100644
index 0000000..45b181e
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticator.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * An AccountAuthenticator class that allows us to register with the AccountManagerService framework
+ *
+ * <p>In order to store contacts on device, we need to associate them with an AccountManager
+ * Account. This allows for easy management, including deletion.
+ *
+ * <p>This class is hosted by a service object, which AccountManagerService can use to interact with
+ * us. In practice though, most/all of this class goes unused and is only an necessary evil so that
+ * AccountManagerService will allow us to explicitly make accounts of the type we specify.
+ */
+public class PbapClientAccountAuthenticator extends AbstractAccountAuthenticator {
+    private static final String TAG = PbapClientAccountAuthenticator.class.getSimpleName();
+
+    public PbapClientAccountAuthenticator(Context context) {
+        super(context);
+    }
+
+    // Editing properties is not supported
+    @Override
+    public Bundle editProperties(AccountAuthenticatorResponse r, String accountType) {
+        Log.d(TAG, "editProperties(accountType=" + accountType + ")");
+        throw new UnsupportedOperationException();
+    }
+
+    // Don't add additional accounts
+    @Override
+    public Bundle addAccount(
+            AccountAuthenticatorResponse r,
+            String accountType,
+            String authTokenType,
+            String[] requiredFeatures,
+            Bundle options)
+            throws NetworkErrorException {
+        Log.d(
+                TAG,
+                "addAccount(accountType="
+                        + accountType
+                        + ", authTokenType="
+                        + authTokenType
+                        + ", requiredFeatures="
+                        + Arrays.toString(requiredFeatures)
+                        + ")");
+        // Don't allow accounts to be added.
+        throw new UnsupportedOperationException();
+    }
+
+    // Ignore attempts to confirm credentials
+    @Override
+    public Bundle confirmCredentials(
+            AccountAuthenticatorResponse r, Account account, Bundle options)
+            throws NetworkErrorException {
+        Log.d(TAG, "confirmCredentials(account=" + account + ", options=" + options + ")");
+        return null;
+    }
+
+    // Getting an authentication token is not supported
+    @Override
+    public Bundle getAuthToken(
+            AccountAuthenticatorResponse r, Account account, String authTokenType, Bundle options)
+            throws NetworkErrorException {
+        Log.d(
+                TAG,
+                "getAuthToken(account="
+                        + account
+                        + ", authTokenType="
+                        + authTokenType
+                        + ", options="
+                        + options
+                        + ")");
+        throw new UnsupportedOperationException();
+    }
+
+    // Getting a label for the auth token is not supported
+    @Override
+    public String getAuthTokenLabel(String authTokenType) {
+        Log.d(TAG, "getAuthTokenLabel(authTokenType=" + authTokenType + ")");
+        return null;
+    }
+
+    // Updating user credentials is not supported
+    @Override
+    public Bundle updateCredentials(
+            AccountAuthenticatorResponse r, Account account, String authTokenType, Bundle options)
+            throws NetworkErrorException {
+        Log.d(
+                TAG,
+                "updateCredentials(account="
+                        + account
+                        + ", authTokenType="
+                        + authTokenType
+                        + ", options="
+                        + options
+                        + ")");
+        return null;
+    }
+
+    // Checking features for the account is not supported
+    @Override
+    public Bundle hasFeatures(AccountAuthenticatorResponse r, Account account, String[] features)
+            throws NetworkErrorException {
+        Log.d(
+                TAG,
+                "hasFeatures(Account=" + account + ", features=" + Arrays.toString(features) + ")");
+
+        final Bundle result = new Bundle();
+        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+        return result;
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/AuthenticationService.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorService.java
similarity index 73%
rename from android/app/src/com/android/bluetooth/pbapclient/AuthenticationService.java
rename to android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorService.java
index c876457..216dc2c 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/AuthenticationService.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorService.java
@@ -19,12 +19,17 @@
 import android.content.Intent;
 import android.os.IBinder;
 
-public class AuthenticationService extends Service {
-    private Authenticator mAuthenticator;
+/**
+ * A service to host out AccountManagerService compliant Authenticator
+ *
+ * <p>See PbapClientAccountAuthenticator for details.
+ */
+public class PbapClientAccountAuthenticatorService extends Service {
+    private PbapClientAccountAuthenticator mAuthenticator;
 
     @Override
     public void onCreate() {
-        mAuthenticator = new Authenticator(this);
+        mAuthenticator = new PbapClientAccountAuthenticator(this);
     }
 
     @Override
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountManager.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountManager.java
new file mode 100644
index 0000000..da0c2a1
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientAccountManager.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.bluetooth.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class abstracts away interactions and management of the AccountManager Account objects that
+ * we need to store contacts and call logs on Android. This object provides functions to get/create
+ * an account, as well as remove or cleanup accounts.
+ *
+ * <p>Most AccountManager functions we want to use require the caller (us) to have a signature match
+ * with the authenticator that owns the specified account. AccountManager knowing this is contingent
+ * on our AuthenticationService being started (Our PbapClientAccountAuthenticatorService, which owns
+ * our PbapClientAccountAuthenticator) and AccountManagerService being notified of it so it can
+ * update its cache. This happens asynchronously and can sometimes take as long as 30 seconds after
+ * stack startup to be available. This object also abstracts this issue away, handling the timing
+ * and notifying clients when accounts are ready.
+ *
+ * <p>Once the account list has been intitialized, clients can begin making calls to add, remove and
+ * list accounts.
+ */
+class PbapClientAccountManager {
+    private static final String TAG = PbapClientAccountManager.class.getSimpleName();
+
+    private final Context mContext;
+    private final AccountManager mAccountManager;
+    private final UserManager mUserManager;
+    private final String mAccountType;
+    private final AccountManagerReceiver mAccountManagerReceiver = new AccountManagerReceiver();
+
+    private HandlerThread mHandlerThread = null;
+    private AccountHandler mAccountHandler = null;
+
+    private final Object mAccountLock = new Object();
+
+    @GuardedBy("mAccountLock")
+    private final Set<Account> mAccounts = new HashSet<Account>();
+
+    private boolean mIsUserReady = false;
+    private volatile boolean mAccountsInitialized = false;
+
+    // For sending events back to the object owner
+    private final Callback mCallback;
+
+    /** A Callback interface so clients can receive structured events from this account manager */
+    interface Callback {
+        /**
+         * Receive account visibility updates
+         *
+         * @param oldAccounts The list of previously available accounts, or null if this is the
+         *     first account update after initialization
+         * @param newAccounts The list of newly available accounts
+         */
+        void onAccountsChanged(List<Account> oldAccounts, List<Account> newAccounts);
+    }
+
+    PbapClientAccountManager(Context context, Callback callback) {
+        this(context, null, callback);
+    }
+
+    @VisibleForTesting
+    PbapClientAccountManager(Context context, HandlerThread handlerThread, Callback callback) {
+        mContext = Objects.requireNonNull(context);
+        mAccountManager = mContext.getSystemService(AccountManager.class);
+        mUserManager = mContext.getSystemService(UserManager.class);
+        mAccountType = mContext.getResources().getString(R.string.pbap_client_account_type);
+        mHandlerThread = handlerThread;
+        mCallback = callback;
+    }
+
+    public void start() {
+        Log.d(TAG, "start()");
+
+        mAccountsInitialized = false;
+        synchronized (mAccountLock) {
+            mAccounts.clear();
+        }
+
+        // Allow injecting a TestLooper
+        if (mHandlerThread == null) {
+            mHandlerThread = new HandlerThread(TAG);
+        }
+
+        mHandlerThread.start();
+        mAccountHandler = new AccountHandler(mHandlerThread.getLooper());
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        mContext.registerReceiver(mAccountManagerReceiver, filter);
+
+        if (isUserUnlocked()) {
+            mAccountHandler.obtainMessage(AccountHandler.MSG_USER_UNLOCKED).sendToTarget();
+        }
+    }
+
+    public void stop() {
+        Log.d(TAG, "stop()");
+
+        mContext.unregisterReceiver(mAccountManagerReceiver);
+        if (mAccountHandler != null) {
+            mAccountHandler.removeCallbacksAndMessages(null);
+            mAccountHandler = null;
+        }
+
+        if (mHandlerThread != null) {
+            mHandlerThread.quit();
+            mHandlerThread = null;
+        }
+
+        mAccountsInitialized = false;
+    }
+
+    /**
+     * Determine if this object has completed initialization of the accounts list.
+     *
+     * <p>Initialization happens once the user is unlock and our account type is recognized by the
+     * AccountManager framework.
+     *
+     * @return True if the accounts list has been initialized, false otherwise.
+     */
+    public boolean isAccountTypeInitialized() {
+        return mAccountsInitialized;
+    }
+
+    /**
+     * Get a well-formed Pbap Client based Account object to add for a given remote device.
+     *
+     * <p>This account should be used when making storage calls. Be sure the account is added and
+     * exists before using it for storage calls.
+     *
+     * @param device The remote device you would like a PBAP Client account for
+     * @return an Account object corresponding to the given remote device
+     */
+    public Account getAccountForDevice(BluetoothDevice device) {
+        if (device == null) {
+            throw new IllegalArgumentException("Null device");
+        }
+        return new Account(device.getAddress(), mAccountType);
+    }
+
+    /**
+     * Get the list of available PBAP Client accounts
+     *
+     * @return A list of all available PBAP Client based accounts on this device
+     */
+    public List<Account> getAccounts() {
+        if (!mAccountsInitialized) {
+            Log.w(TAG, "getAccounts(): Not initialized");
+            return Collections.emptyList();
+        }
+        synchronized (mAccountLock) {
+            return Collections.unmodifiableList(new ArrayList<>(mAccounts));
+        }
+    }
+
+    /**
+     * Request for an account to be added
+     *
+     * <p>Storage must be initialized before calls to this function will be successful
+     *
+     * @param account The account to add
+     * @return True if the account is successfully added, False otherwise
+     */
+    public boolean addAccount(Account account) {
+        if (!mAccountsInitialized) {
+            Log.w(TAG, "addAccount(account=" + account + "): Cannot add account, not initialized");
+            return false;
+        }
+        synchronized (mAccountLock) {
+            List<Account> oldAccounts = new ArrayList<>(mAccounts);
+            if (addAccountInternal(account)) {
+                notifyAccountsChanged(oldAccounts, new ArrayList<>(mAccounts));
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Request for an account to be removed
+     *
+     * <p>Storage must be initialized before calls to this function will be successful
+     *
+     * @param account The account to remove
+     * @return True if the account is successfully removed, False otherwise
+     */
+    public boolean removeAccount(Account account) {
+        if (!mAccountsInitialized) {
+            Log.w(
+                    TAG,
+                    "removeAccount(account="
+                            + account
+                            + "): Cannot remove account, not initialized");
+            return false;
+        }
+        synchronized (mAccountLock) {
+            List<Account> oldAccounts = new ArrayList<>(mAccounts);
+            if (removeAccountInternal(account)) {
+                notifyAccountsChanged(oldAccounts, new ArrayList<>(mAccounts));
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /** Receive user lifecycle events and forward them to the handler for processing */
+    private class AccountManagerReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.v(TAG, "onReceive action=" + action);
+            if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
+                mAccountHandler.obtainMessage(AccountHandler.MSG_USER_UNLOCKED).sendToTarget();
+            }
+        }
+    }
+
+    /**
+     * A handler to serialize account events. This allows us to wait for our authentication service
+     * to be available until we interact with accounts, and then safely create and remove accounts
+     * as needed.
+     */
+    private class AccountHandler extends Handler {
+        // There's an ~1-2 second latency between when our Authentication service is set as
+        // available to the system and when the Authentication/Account framework code will recognize
+        // it and allow us to alter accounts. In lieu of the Accounts team dealing with this race
+        // condition, we're going to periodically poll over 3 seconds until our accounts are
+        // visible, remove old accounts, and then notify device state machines that they can create
+        // accounts and download contacts.
+        //
+        // TODO(233361365): Remove this pattern when the framework solves their race condition
+        private static final int ACCOUNT_ADD_RETRY_MS = 1000;
+
+        public static final int MSG_USER_UNLOCKED = 0;
+        public static final int MSG_ACCOUNT_CHECK = 1;
+
+        AccountHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            Log.v(TAG, "Process message=" + messageToString(msg.what));
+            switch (msg.what) {
+                case MSG_USER_UNLOCKED:
+                    handleUserUnlocked();
+                    break;
+                case MSG_ACCOUNT_CHECK:
+                    handleAccountCheck();
+                    break;
+                default:
+                    Log.e(TAG, "received an unknown message : " + msg.what);
+            }
+        }
+
+        private void handleUserUnlocked() {
+            if (mIsUserReady) {
+                Log.i(TAG, "Notified user unlocked, but we've already processed this event. Skip");
+                return;
+            }
+
+            Log.i(TAG, "User is unlocked. Begin account check process");
+            mIsUserReady = true;
+            this.obtainMessage(MSG_ACCOUNT_CHECK).sendToTarget();
+        }
+
+        private void handleAccountCheck() {
+            if (mAccountsInitialized) {
+                Log.w(TAG, "Accounts already initialized. Skipping");
+                return;
+            }
+
+            if (isAccountAuthenticationServiceReady()) {
+                Log.d(TAG, "Account type ready to be interacted with. Initialize account list");
+
+                Account[] availableAccounts = mAccountManager.getAccountsByType(mAccountType);
+                synchronized (mAccountLock) {
+                    for (Account account : availableAccounts) {
+                        Log.i(TAG, "Loaded saved account, account=" + account);
+                        mAccounts.add(account);
+                    }
+
+                    mAccountsInitialized = true;
+
+                    Log.d(TAG, "Accounts list initialized");
+                    notifyAccountsChanged(null, new ArrayList<>(mAccounts));
+                }
+            } else {
+                Log.d(TAG, "Accounts not ready. Check again in " + ACCOUNT_ADD_RETRY_MS + "ms");
+                sendMessageDelayed(obtainMessage(MSG_ACCOUNT_CHECK), ACCOUNT_ADD_RETRY_MS);
+            }
+        }
+
+        private static String messageToString(int msg) {
+            switch (msg) {
+                case MSG_USER_UNLOCKED:
+                    return "MSG_USER_UNLOCKED";
+                case MSG_ACCOUNT_CHECK:
+                    return "MSG_ACCOUNT_CHECK";
+                default:
+                    return "MSG_RESERVED_" + msg;
+            }
+        }
+    }
+
+    /**
+     * Determine if the user is unlocked
+     *
+     * <p>AccountManager functionality doesn't work until the user is unlocked. We need to hold our
+     * calls until we know the user is unlocked.
+     *
+     * @return True if the use it unlocked, False otherwise
+     */
+    private boolean isUserUnlocked() {
+        return mUserManager.isUserUnlocked();
+    }
+
+    /**
+     * Determine if we're able to interact with our own account type
+     *
+     * <p>We're able to interact with our account when our account service is up and the
+     * AccountManagerService has finished updating itself such that it also knows our service is
+     * ready. The AccountManager framework doesn't have a good way for us to know _exactly_ when
+     * this is, so the best we can do is try to interact with our account type and see if it works.
+     *
+     * <p>We use a fake device address and our accoun ttype here to see if our account is visible
+     * yet.
+     *
+     * <p>This function is used in conjunction with the handler and a polling scheme to see
+     * determine when we're finally ready.
+     *
+     * <p>Note: that this function uses the same restrictions as the other add and remove functions,
+     * but is *also* available to all system apps instead of throwing a runtime SecurityException.
+     * AccountManagerService makes an !isSystemUid check before throwing.
+     *
+     * @return True if our PBAP Client Account type is ready to use, False otherwise.
+     */
+    private boolean isAccountAuthenticationServiceReady() {
+        Account account = new Account("00:00:00:00:00:00", mAccountType);
+        int visibility = mAccountManager.getAccountVisibility(account, mContext.getPackageName());
+        Log.d(TAG, "Checking visibility, visibility=" + visibility);
+        return visibility == AccountManager.VISIBILITY_VISIBLE
+                || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
+    }
+
+    /**
+     * Explicitly add an account. Returns true is successful, false otherwise.
+     *
+     * <p>Any exceptions generated cause this function to fail silently. In particular,
+     * SecurityExceptions due to the fact that our authentication service isn't recognized by the
+     * AccountManager framework yet are dropped. Our handler is setup to make it so we shouldn't
+     * make these calls unless we know AccountManager knows of us though.
+     *
+     * @param account The account to add
+     * @return True on success, false otherwise
+     */
+    private boolean addAccountInternal(Account account) {
+        try {
+            synchronized (mAccountLock) {
+                if (mAccountManager.addAccountExplicitly(account, null, null)) {
+                    mAccounts.add(account);
+                    Log.i(TAG, "Added account=" + account);
+                    return true;
+                }
+                Log.w(TAG, "Failed to add account=" + account);
+                return false;
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Exception while trying to add account=" + account, e);
+            return false;
+        }
+    }
+
+    /**
+     * Explicitly remove an account. Returns true is successful, false otherwise.
+     *
+     * <p>Any exceptions generated cause this function to fail silently. In particular,
+     * SecurityExceptions due to the fact that our authentication service isn't recognized by the
+     * AccountManager framework yet are dropped. Our handler is setup to make it so we shouldn't
+     * make these calls unless we know AccountManager knows of us though.
+     *
+     * @param account the account to explicitly remove
+     * @return True on success, false otherwise
+     */
+    private boolean removeAccountInternal(Account account) {
+        try {
+            synchronized (mAccountLock) {
+                if (mAccountManager.removeAccountExplicitly(account)) {
+                    mAccounts.remove(account);
+                    Log.i(TAG, "Removed account=" + account);
+                    return true;
+                }
+                Log.w(TAG, "Failed to remove account=" + account);
+                return false;
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Exception while trying to remove account=" + account, e);
+            return false;
+        }
+    }
+
+    /**
+     * Notify all client callbacks that the set of accounts has changed
+     *
+     * @param oldAccounts The previous list of accounts available, or null if this is the first
+     *     update
+     * @param newAccounts The new list of accounts available
+     */
+    private void notifyAccountsChanged(List<Account> oldAccounts, List<Account> newAccounts) {
+        Log.v(TAG, "notifyAccountsChanged, old=" + oldAccounts + ", new=" + newAccounts);
+        if (mCallback != null) {
+            mCallback.onAccountsChanged(oldAccounts, newAccounts);
+        }
+    }
+
+    /** Get a debug dump of this class, containing the accounts on the device */
+    public String dump() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(TAG).append(":\n");
+        sb.append("        Account Type: ").append(mAccountType).append("\n");
+        sb.append("        User Unlocked: ").append(isUserUnlocked()).append("\n");
+        sb.append("        Account Type Ready: ")
+                .append(isAccountAuthenticationServiceReady())
+                .append("\n");
+        sb.append("        Accounts Initialized: ").append(mAccountsInitialized).append("\n");
+        sb.append("        Accounts:\n");
+        for (Account account : getAccounts()) {
+            sb.append("          ").append(account).append("\n");
+        }
+        return sb.toString();
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientBinder.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientBinder.java
new file mode 100644
index 0000000..df09be4
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientBinder.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.IBluetoothPbapClient;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/** Handler for incoming service calls destined for PBAP Client */
+public class PbapClientBinder extends IBluetoothPbapClient.Stub implements IProfileServiceBinder {
+    private static final String TAG = PbapClientBinder.class.getSimpleName();
+
+    private PbapClientService mService;
+
+    PbapClientBinder(PbapClientService service) {
+        mService = service;
+    }
+
+    @Override
+    public void cleanup() {
+        mService = null;
+    }
+
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+    private PbapClientService getService(AttributionSource source) {
+        // Cache mService because it can change while getService is called
+        PbapClientService service = mService;
+
+        if (Utils.isInstrumentationTestMode()) {
+            return service;
+        }
+
+        if (!Utils.checkServiceAvailable(service, TAG)) {
+            Log.w(TAG, "getService() failed, service not available");
+            return null;
+        }
+
+        if (!Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
+                || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
+            Log.w(TAG, "getService() failed, rejected due to permissions");
+            return null;
+        }
+
+        service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
+        return service;
+    }
+
+    @Override
+    public boolean connect(BluetoothDevice device, AttributionSource source) {
+        Log.d(TAG, "connect(device=" + device + ")");
+        PbapClientService service = getService(source);
+        if (service == null) {
+            return false;
+        }
+        return service.connect(device);
+    }
+
+    @Override
+    public boolean disconnect(BluetoothDevice device, AttributionSource source) {
+        Log.d(TAG, "disconnect(device=" + device + ")");
+        PbapClientService service = getService(source);
+        if (service == null) {
+            return false;
+        }
+        return service.disconnect(device);
+    }
+
+    @Override
+    public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
+        Log.d(TAG, "getConnectedDevices()");
+        PbapClientService service = getService(source);
+        if (service == null) {
+            return Collections.emptyList();
+        }
+        return service.getConnectedDevices();
+    }
+
+    @Override
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(
+            int[] states, AttributionSource source) {
+        Log.d(TAG, "getDevicesMatchingConnectionStates(states=" + Arrays.toString(states) + ")");
+        PbapClientService service = getService(source);
+        if (service == null) {
+            return Collections.emptyList();
+        }
+        return service.getDevicesMatchingConnectionStates(states);
+    }
+
+    @Override
+    public int getConnectionState(BluetoothDevice device, AttributionSource source) {
+        Log.d(TAG, "getConnectionState(device=" + device + ")");
+        PbapClientService service = getService(source);
+        if (service == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+        return service.getConnectionState(device);
+    }
+
+    @Override
+    public boolean setConnectionPolicy(
+            BluetoothDevice device, int connectionPolicy, AttributionSource source) {
+        Log.d(TAG, "setConnectionPolicy(device=" + device + ", policy=" + connectionPolicy + ")");
+
+        PbapClientService service = getService(source);
+        if (service == null) {
+            return false;
+        }
+        return service.setConnectionPolicy(device, connectionPolicy);
+    }
+
+    @Override
+    public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
+        Log.d(TAG, "getConnectionPolicy(device=" + device + ")");
+        PbapClientService service = getService(source);
+        if (service == null) {
+            return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+        }
+        return service.getConnectionPolicy(device);
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
index 961337c..b343c07 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
@@ -16,22 +16,13 @@
 
 package com.android.bluetooth.pbapclient;
 
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
-
 import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothPbapClient;
-import android.content.AttributionSource;
-import android.content.BroadcastReceiver;
+import android.bluetooth.SdpPseRecord;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
@@ -40,9 +31,6 @@
 import android.sysprop.BluetoothProperties;
 import android.util.Log;
 
-import com.android.bluetooth.BluetoothMethodProxy;
-import com.android.bluetooth.R;
-import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
@@ -51,7 +39,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -65,7 +52,7 @@
 
     /** The component names for the owned authenticator service */
     private static final String AUTHENTICATOR_SERVICE =
-            AuthenticationService.class.getCanonicalName();
+            PbapClientAccountAuthenticatorService.class.getCanonicalName();
 
     // MAXIMUM_DEVICES set to 10 to prevent an excessive number of simultaneous devices.
     private static final int MAXIMUM_DEVICES = 10;
@@ -75,61 +62,34 @@
             new ConcurrentHashMap<>();
 
     private static PbapClientService sPbapClientService;
-    @VisibleForTesting PbapBroadcastReceiver mPbapBroadcastReceiver = new PbapBroadcastReceiver();
+    private final PbapClientAccountManager mPbapClientAccountManager;
     private int mSdpHandle = -1;
-
     private DatabaseManager mDatabaseManager;
-
-    /**
-     * There's an ~1-2 second latency between when our Authentication service is set as available to
-     * the system and when the Authentication/Account framework code will recognize it and allow us
-     * to alter accounts. In lieu of the Accounts team dealing with this race condition, we're going
-     * to periodically poll over 3 seconds until our accounts are visible, remove old accounts, and
-     * then notify device state machines that they can create accounts and download contacts.
-     */
-    // TODO(233361365): Remove this pattern when the framework solves their race condition
-    private static final int ACCOUNT_VISIBILITY_CHECK_MS = 500;
-
-    private static final int ACCOUNT_VISIBILITY_CHECK_TRIES_MAX = 6;
-    private int mAccountVisibilityCheckTries = 0;
-    private final Handler mAuthServiceHandler = new Handler();
     private Handler mHandler;
-    private final Runnable mCheckAuthService =
-            new Runnable() {
-                @Override
-                public void run() {
-                    // If our accounts are finally visible to use, clean up old ones and tell
-                    // devices they can issue downloads if they're ready. Otherwise, wait and try
-                    // again.
-                    if (isAuthenticationServiceReady()) {
-                        Log.i(
-                                TAG,
-                                "Service ready! Clean up old accounts and try contacts downloads");
-                        removeUncleanAccounts();
-                        for (PbapClientStateMachine stateMachine :
-                                mPbapClientStateMachineMap.values()) {
-                            stateMachine.tryDownloadIfConnected();
-                        }
-                    } else if (mAccountVisibilityCheckTries < ACCOUNT_VISIBILITY_CHECK_TRIES_MAX) {
-                        mAccountVisibilityCheckTries += 1;
-                        Log.w(
-                                TAG,
-                                "AccountManager hasn't registered our service yet. Retry "
-                                        + mAccountVisibilityCheckTries
-                                        + "/"
-                                        + ACCOUNT_VISIBILITY_CHECK_TRIES_MAX);
-                        mAuthServiceHandler.postDelayed(this, ACCOUNT_VISIBILITY_CHECK_MS);
-                    } else {
-                        Log.e(
-                                TAG,
-                                "Failed to register Authentication Service and get account"
-                                        + " visibility");
-                    }
-                }
-            };
 
-    public PbapClientService(Context ctx) {
-        super(ctx);
+    class PbapClientAccountManagerCallback implements PbapClientAccountManager.Callback {
+        @Override
+        public void onAccountsChanged(List<Account> oldAccounts, List<Account> newAccounts) {
+            Log.i(TAG, "onAccountsChanged: old=" + oldAccounts + ", new=" + newAccounts);
+            if (oldAccounts == null) {
+                removeUncleanAccounts();
+                for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) {
+                    stateMachine.tryDownloadIfConnected();
+                }
+            }
+        }
+    }
+
+    public PbapClientService(Context context) {
+        super(context);
+        mPbapClientAccountManager =
+                new PbapClientAccountManager(context, new PbapClientAccountManagerCallback());
+    }
+
+    @VisibleForTesting
+    PbapClientService(Context context, PbapClientAccountManager accountManager) {
+        super(context);
+        mPbapClientAccountManager = accountManager;
     }
 
     public static boolean isEnabled() {
@@ -138,7 +98,7 @@
 
     @Override
     public IProfileServiceBinder initBinder() {
-        return new BluetoothPbapClientBinder(this);
+        return new PbapClientBinder(this);
     }
 
     @Override
@@ -153,17 +113,9 @@
         setComponentAvailable(AUTHENTICATOR_SERVICE, true);
 
         mHandler = new Handler(Looper.getMainLooper());
-        IntentFilter filter = new IntentFilter();
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        // delay initial download until after the user is unlocked to add an account.
-        filter.addAction(Intent.ACTION_USER_UNLOCKED);
-        try {
-            registerReceiver(mPbapBroadcastReceiver, filter);
-        } catch (Exception e) {
-            Log.w(TAG, "Unable to register pbapclient receiver", e);
-        }
 
-        initializeAuthenticationService();
+        mPbapClientAccountManager.start();
+
         registerSdpRecord();
         setPbapClientService(this);
     }
@@ -172,11 +124,7 @@
     public void stop() {
         setPbapClientService(null);
         cleanUpSdpRecord();
-        try {
-            unregisterReceiver(mPbapBroadcastReceiver);
-        } catch (Exception e) {
-            Log.w(TAG, "Unable to unregister pbapclient receiver", e);
-        }
+
         for (PbapClientStateMachine pbapClientStateMachine : mPbapClientStateMachineMap.values()) {
             pbapClientStateMachine.doQuit();
         }
@@ -188,113 +136,32 @@
             mHandler = null;
         }
 
-        cleanupAuthenticationService();
+        removeUncleanAccounts();
+        mPbapClientAccountManager.stop();
+
         setComponentAvailable(AUTHENTICATOR_SERVICE, false);
     }
 
-    void cleanupDevice(BluetoothDevice device) {
-        Log.d(TAG, "Cleanup device: " + device);
-        synchronized (mPbapClientStateMachineMap) {
-            PbapClientStateMachine pbapClientStateMachine = mPbapClientStateMachineMap.get(device);
-            if (pbapClientStateMachine != null) {
-                mPbapClientStateMachineMap.remove(device);
-                pbapClientStateMachine.doQuit();
-            }
-        }
-    }
-
     /**
-     * Periodically check if the account framework has recognized our service and will allow us to
-     * interact with our accounts. Notify state machines once our service is ready so we can trigger
-     * account downloads.
-     */
-    private void initializeAuthenticationService() {
-        mAuthServiceHandler.postDelayed(mCheckAuthService, ACCOUNT_VISIBILITY_CHECK_MS);
-    }
-
-    private void cleanupAuthenticationService() {
-        mAuthServiceHandler.removeCallbacks(mCheckAuthService);
-        removeUncleanAccounts();
-    }
-
-    /**
-     * Determine if our account type is visible to us yet. If it is, then our service is ready and
-     * our account type is ready to use.
+     * Add our PBAP Client SDP record to the device SDP database
      *
-     * <p>Make a placeholder device account and determine our visibility relative to it. Note that
-     * this function uses the same restrictions as the other add and remove functions, but is *also*
-     * available to all system apps instead of throwing a runtime SecurityException.
+     * <p>This allows our client to be recognized by the remove device. The record must be cleaned
+     * up when we shutdown.
      */
-    protected boolean isAuthenticationServiceReady() {
-        Account account = new Account("00:00:00:00:00:00", getString(R.string.pbap_account_type));
-        AccountManager accountManager = AccountManager.get(this);
-        int visibility = accountManager.getAccountVisibility(account, getPackageName());
-        Log.d(TAG, "Checking visibility, visibility=" + visibility);
-        return visibility == AccountManager.VISIBILITY_VISIBLE
-                || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
-    }
-
-    private void removeUncleanAccounts() {
-        if (!isAuthenticationServiceReady()) {
-            Log.w(TAG, "Can't remove accounts. AccountManager hasn't registered our service yet.");
-            return;
-        }
-
-        // Find all accounts that match the type "pbap" and delete them.
-        AccountManager accountManager = AccountManager.get(this);
-        Account[] accounts =
-                accountManager.getAccountsByType(getString(R.string.pbap_account_type));
-        Log.v(TAG, "Found " + accounts.length + " unclean accounts");
-        for (Account acc : accounts) {
-            Log.w(TAG, "Deleting " + acc);
-            try {
-                getContentResolver()
-                        .delete(
-                                CallLog.Calls.CONTENT_URI,
-                                CallLog.Calls.PHONE_ACCOUNT_ID + "=?",
-                                new String[] {acc.name});
-            } catch (IllegalArgumentException e) {
-                Log.w(TAG, "Call Logs could not be deleted, they may not exist yet.");
-            }
-            // The device ID is the name of the account.
-            accountManager.removeAccountExplicitly(acc);
-        }
-    }
-
-    private void removeHfpCallLog(String accountName, Context context) {
-        Log.d(TAG, "Removing call logs from " + accountName);
-        // Delete call logs belonging to accountName==BD_ADDR that also match
-        // component name "hfpclient".
-        ComponentName componentName = new ComponentName(context, HfpClientConnectionService.class);
-        String selectionFilter =
-                CallLog.Calls.PHONE_ACCOUNT_ID
-                        + "=? AND "
-                        + CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME
-                        + "=?";
-        String[] selectionArgs = new String[] {accountName, componentName.flattenToString()};
-        try {
-            BluetoothMethodProxy.getInstance()
-                    .contentResolverDelete(
-                            getContentResolver(),
-                            CallLog.Calls.CONTENT_URI,
-                            selectionFilter,
-                            selectionArgs);
-        } catch (IllegalArgumentException e) {
-            Log.w(TAG, "Call Logs could not be deleted, they may not exist yet.");
-        }
-    }
-
     private void registerSdpRecord() {
         SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
         if (!nativeInterface.isAvailable()) {
             Log.e(TAG, "SdpManagerNativeInterface is not available");
             return;
         }
-        mSdpHandle =
-                nativeInterface.createPbapPceRecord(
-                        SERVICE_NAME, PbapClientConnectionHandler.PBAP_V1_2);
+        mSdpHandle = nativeInterface.createPbapPceRecord(SERVICE_NAME, PbapSdpRecord.VERSION_1_2);
     }
 
+    /**
+     * Remove our PBAP Client SDP record from the device SDP database
+     *
+     * <p>Gracefully removes PBAP Client support from our SDP records. Called when shutting down.
+     */
     private void cleanUpSdpRecord() {
         if (mSdpHandle < 0) {
             Log.e(TAG, "cleanUpSdpRecord, SDP record never created");
@@ -317,20 +184,137 @@
         }
     }
 
-    @VisibleForTesting
-    class PbapBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            Log.v(TAG, "onReceive" + action);
-            if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
-                for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) {
-                    stateMachine.tryDownloadIfConnected();
-                }
+    void cleanupDevice(BluetoothDevice device) {
+        Log.d(TAG, "Cleanup device: " + device);
+        synchronized (mPbapClientStateMachineMap) {
+            PbapClientStateMachine pbapClientStateMachine = mPbapClientStateMachineMap.get(device);
+            if (pbapClientStateMachine != null) {
+                mPbapClientStateMachineMap.remove(device);
+                pbapClientStateMachine.doQuit();
             }
         }
     }
 
+    /**
+     * Clean up any existing accounts.
+     *
+     * <p>This function gets the list of available Pbap Client accounts and deletes them. Deletion
+     * of the account causes Contacts Provider to also delete the associated contacts data. We
+     * separately clean up the call log data associated with a given account too.
+     */
+    private void removeUncleanAccounts() {
+        List<Account> accounts = mPbapClientAccountManager.getAccounts();
+        Log.i(TAG, "removeUncleanAccounts: Found " + accounts.size() + " accounts");
+
+        for (Account account : accounts) {
+            Log.d(TAG, "removeUncleanAccounts: removing call logs for account=" + account);
+            try {
+                // The device ID for call logs is the name of the account
+                getContentResolver()
+                        .delete(
+                                CallLog.Calls.CONTENT_URI,
+                                CallLog.Calls.PHONE_ACCOUNT_ID + "=?",
+                                new String[] {account.name});
+            } catch (IllegalArgumentException e) {
+                Log.w(TAG, "Call Logs could not be deleted, they may not exist yet.", e);
+            }
+
+            Log.i(TAG, "removeUncleanAccounts: removing account=" + account);
+            mPbapClientAccountManager.removeAccount(account);
+        }
+    }
+
+    private void removeHfpCallLog(String accountName) {
+        Log.d(TAG, "Removing call logs from " + accountName);
+        // Delete call logs belonging to accountName==BD_ADDR that also match component "hfpclient"
+        ComponentName componentName = new ComponentName(this, HfpClientConnectionService.class);
+        String selectionFilter =
+                CallLog.Calls.PHONE_ACCOUNT_ID
+                        + "=? AND "
+                        + CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME
+                        + "=?";
+        String[] selectionArgs = new String[] {accountName, componentName.flattenToString()};
+        try {
+            getContentResolver().delete(CallLog.Calls.CONTENT_URI, selectionFilter, selectionArgs);
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "Call Logs could not be deleted, they may not exist yet.");
+        }
+    }
+
+    /**
+     * Ensure that after HFP disconnects, we remove call logs. This addresses the situation when
+     * PBAP was never connected while calls were made. Ideally {@link PbapClientConnectionHandler}
+     * has code to remove calllogs when PBAP disconnects.
+     */
+    public void handleHeadsetClientConnectionStateChanged(
+            BluetoothDevice device, int oldState, int newState) {
+        if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+            Log.d(TAG, "Received intent to disconnect HFP with " + device);
+            // HFP client stores entries in calllog.db by BD_ADDR and component name
+            // Using the current Service as the context.
+            removeHfpCallLog(device.getAddress());
+        }
+    }
+
+    /**
+     * Determine if our account type is available and ready to be interacted with
+     *
+     * @return True is account type is ready, false otherwise
+     */
+    public boolean isAccountTypeReady() {
+        return mPbapClientAccountManager.isAccountTypeInitialized();
+    }
+
+    /**
+     * Add an account
+     *
+     * @param account The account you wish to add
+     * @return True if the account addition was successful, False otherwise
+     */
+    public boolean addAccount(Account account) {
+        return mPbapClientAccountManager.addAccount(account);
+    }
+
+    /**
+     * Remove an account
+     *
+     * @param account The account you wish to remove
+     * @return True if the account removal was successful, False otherwise
+     */
+    public boolean removeAccount(Account account) {
+        return mPbapClientAccountManager.removeAccount(account);
+    }
+
+    /**
+     * Get debug information about this PbapClientService instance
+     *
+     * @param sb The StringBuilder instance to add our debug dump info to
+     */
+    @Override
+    public void dump(StringBuilder sb) {
+        super.dump(sb);
+        for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) {
+            stateMachine.dump(sb);
+        }
+        ProfileService.println(sb, mPbapClientAccountManager.dump());
+    }
+
+    // *********************************************************************************************
+    // * Events from AdapterService
+    // *********************************************************************************************
+
+    /**
+     * Get notified of incoming ACL disconnections
+     *
+     * <p>OBEX client's are supposed to be in control of the connection lifecycle, and servers are
+     * not supposed to disconnect OBEX sessions. Despite this, its normal/possible the remote device
+     * to tear down connections at lower levels than OBEX, mainly the L2CAP/RFCOMM links or the ACL.
+     * The OBEX framework isn't setup to be notified of these disconnections, so we must listen for
+     * them separately and clean up the device connection and, if necessary, data when this happens.
+     *
+     * @param device The device that had the ACL disconnect
+     * @param transport The transport the device disconnected on
+     */
     public void aclDisconnected(BluetoothDevice device, int transport) {
         mHandler.post(() -> handleAclDisconnected(device, transport));
     }
@@ -353,132 +337,45 @@
     }
 
     /**
-     * Ensure that after HFP disconnects, we remove call logs. This addresses the situation when
-     * PBAP was never connected while calls were made. Ideally {@link PbapClientConnectionHandler}
-     * has code to remove calllogs when PBAP disconnects.
+     * Get notified of incoming SDP records
+     *
+     * <p>This function looks for PBAP Server records coming from remote devices, and forwards them
+     * to the appropriate device's state machine instance for processing. SDP records are used to
+     * determine which L2CAP/RFCOMM psm/channel to connect on, as well as which phonebooks to expect
      */
-    public void handleHeadsetClientConnectionStateChanged(
-            BluetoothDevice device, int oldState, int newState) {
-        if (newState == BluetoothProfile.STATE_DISCONNECTED) {
-            Log.d(TAG, "Received intent to disconnect HFP with " + device);
-            // HFP client stores entries in calllog.db by BD_ADDR and component name
-            // Using the current Service as the context.
-            removeHfpCallLog(device.getAddress(), this);
+    public void receiveSdpSearchRecord(
+            BluetoothDevice device, int status, Parcelable record, ParcelUuid uuid) {
+        Log.v(
+                TAG,
+                "Received SDP record for UUID="
+                        + uuid.toString()
+                        + " (expected UUID="
+                        + BluetoothUuid.PBAP_PSE.toString()
+                        + ")");
+        if (uuid.equals(BluetoothUuid.PBAP_PSE)) {
+            PbapClientStateMachine stateMachine = mPbapClientStateMachineMap.get(device);
+            if (stateMachine == null) {
+                Log.e(TAG, "No Statemachine found for the device=" + device.toString());
+                return;
+            }
+            SdpPseRecord pseRecord = (SdpPseRecord) record;
+            if (pseRecord != null) {
+                stateMachine
+                        .obtainMessage(
+                                PbapClientStateMachine.MSG_SDP_COMPLETE,
+                                new PbapSdpRecord(device, pseRecord))
+                        .sendToTarget();
+            } else {
+                Log.w(TAG, "Received null PSE record for device=" + device);
+            }
         }
     }
 
-    /** Handler for incoming service calls */
-    @VisibleForTesting
-    static class BluetoothPbapClientBinder extends IBluetoothPbapClient.Stub
-            implements IProfileServiceBinder {
-        private PbapClientService mService;
+    // *********************************************************************************************
+    // * API methods
+    // *********************************************************************************************
 
-        BluetoothPbapClientBinder(PbapClientService svc) {
-            mService = svc;
-        }
-
-        @Override
-        public void cleanup() {
-            mService = null;
-        }
-
-        @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
-        private PbapClientService getService(AttributionSource source) {
-            // Cache mService because it can change while getService is called
-            PbapClientService service = mService;
-
-            if (Utils.isInstrumentationTestMode()) {
-                return service;
-            }
-
-            if (!Utils.checkServiceAvailable(service, TAG)
-                    || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)
-                    || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
-                return null;
-            }
-
-            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
-
-            return service;
-        }
-
-        @Override
-        public boolean connect(BluetoothDevice device, AttributionSource source) {
-            Log.d(TAG, "PbapClient Binder connect");
-
-            PbapClientService service = getService(source);
-            if (service == null) {
-                Log.e(TAG, "PbapClient Binder connect no service");
-                return false;
-            }
-
-            return service.connect(device);
-        }
-
-        @Override
-        public boolean disconnect(BluetoothDevice device, AttributionSource source) {
-            PbapClientService service = getService(source);
-            if (service == null) {
-                return false;
-            }
-
-            return service.disconnect(device);
-        }
-
-        @Override
-        public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
-            PbapClientService service = getService(source);
-            if (service == null) {
-                return Collections.emptyList();
-            }
-
-            return service.getConnectedDevices();
-        }
-
-        @Override
-        public List<BluetoothDevice> getDevicesMatchingConnectionStates(
-                int[] states, AttributionSource source) {
-            PbapClientService service = getService(source);
-            if (service == null) {
-                return Collections.emptyList();
-            }
-
-            return service.getDevicesMatchingConnectionStates(states);
-        }
-
-        @Override
-        public int getConnectionState(BluetoothDevice device, AttributionSource source) {
-            PbapClientService service = getService(source);
-            if (service == null) {
-                return BluetoothProfile.STATE_DISCONNECTED;
-            }
-
-            return service.getConnectionState(device);
-        }
-
-        @Override
-        public boolean setConnectionPolicy(
-                BluetoothDevice device, int connectionPolicy, AttributionSource source) {
-            PbapClientService service = getService(source);
-            if (service == null) {
-                return false;
-            }
-
-            return service.setConnectionPolicy(device, connectionPolicy);
-        }
-
-        @Override
-        public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
-            PbapClientService service = getService(source);
-            if (service == null) {
-                return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
-            }
-
-            return service.getConnectionPolicy(device);
-        }
-    }
-
-    // API methods
+    /** Get the singleton instance of PbapClientService, if one exists */
     public static synchronized PbapClientService getPbapClientService() {
         if (sPbapClientService == null) {
             Log.w(TAG, "getPbapClientService(): service is null");
@@ -491,12 +388,23 @@
         return sPbapClientService;
     }
 
+    /**
+     * Set the singleton instance of PbapClientService
+     *
+     * <p>This function is meant to be used by tests only.
+     */
     @VisibleForTesting
     static synchronized void setPbapClientService(PbapClientService instance) {
         Log.v(TAG, "setPbapClientService(): set to: " + instance);
         sPbapClientService = instance;
     }
 
+    /**
+     * Requests a connection to the given device's PBAP Server
+     *
+     * @param device is the device with which we will connect to
+     * @return true if we successfully begin the connection process, false otherwise
+     */
     public boolean connect(BluetoothDevice device) {
         if (device == null) {
             throw new IllegalArgumentException("Null device");
@@ -540,13 +448,24 @@
         }
     }
 
+    /**
+     * Get the list of PBAP Server devices this PBAP Client device is connected to
+     *
+     * @return The list of connected PBAP Server devices
+     */
     public List<BluetoothDevice> getConnectedDevices() {
         int[] desiredStates = {BluetoothProfile.STATE_CONNECTED};
         return getDevicesMatchingConnectionStates(desiredStates);
     }
 
-    @VisibleForTesting
-    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+    /**
+     * Get the list of PBAP Server devices this PBAP Client device know about, who are in a given
+     * state.
+     *
+     * @param states The array of BluutoothProfile states you want to match on
+     * @return The list of connected PBAP Server devices
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(0);
         for (Map.Entry<BluetoothDevice, PbapClientStateMachine> stateMachineEntry :
                 mPbapClientStateMachineMap.entrySet()) {
@@ -561,27 +480,6 @@
         return deviceList;
     }
 
-    public void receiveSdpSearchRecord(
-            BluetoothDevice device, int status, Parcelable record, ParcelUuid uuid) {
-        PbapClientStateMachine stateMachine = mPbapClientStateMachineMap.get(device);
-        if (stateMachine == null) {
-            Log.e(TAG, "No Statemachine found for the device=" + device.toString());
-            return;
-        }
-        Log.v(
-                TAG,
-                "Received SDP record for UUID="
-                        + uuid.toString()
-                        + " (expected UUID="
-                        + BluetoothUuid.PBAP_PSE.toString()
-                        + ")");
-        if (uuid.equals(BluetoothUuid.PBAP_PSE)) {
-            stateMachine
-                    .obtainMessage(PbapClientStateMachine.MSG_SDP_COMPLETE, record)
-                    .sendToTarget();
-        }
-    }
-
     /**
      * Get the current connection state of the profile
      *
@@ -651,13 +549,4 @@
         }
         return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.PBAP_CLIENT);
     }
-
-    @Override
-    public void dump(StringBuilder sb) {
-        super.dump(sb);
-        ProfileService.println(sb, "isAuthServiceReady: " + isAuthenticationServiceReady());
-        for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) {
-            stateMachine.dump(sb);
-        }
-    }
 }
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachine.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachine.java
index 2c8a441..0a555c0 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachine.java
@@ -90,6 +90,9 @@
     static final int CONNECT_TIMEOUT = 10000;
     static final int DISCONNECT_TIMEOUT = 3000;
 
+    private static final int LOCAL_SUPPORTED_FEATURES =
+            PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT | PbapSdpRecord.FEATURE_DOWNLOADING;
+
     private final Object mLock;
     private State mDisconnected;
     private State mConnecting;
@@ -168,7 +171,8 @@
                 mConnectionHandler =
                         new PbapClientConnectionHandler.Builder()
                                 .setLooper(looper)
-                                .setContext(mService)
+                                .setLocalSupportedFeatures(LOCAL_SUPPORTED_FEATURES)
+                                .setService(mService)
                                 .setClientSM(PbapClientStateMachine.this)
                                 .setRemoteDevice(mCurrentDevice)
                                 .build();
@@ -311,14 +315,14 @@
     /** Trigger a contacts download if the user is unlocked and our accounts are available to us */
     private void downloadIfReady() {
         boolean userReady = mUserManager.isUserUnlocked();
-        boolean accountServiceReady = mService.isAuthenticationServiceReady();
-        if (!userReady || !accountServiceReady) {
+        boolean accountTypeReady = mService.isAccountTypeReady();
+        if (!userReady || !accountTypeReady) {
             Log.w(
                     TAG,
                     "Cannot download contacts yet, userReady="
                             + userReady
-                            + ", accountServiceReady="
-                            + accountServiceReady);
+                            + ", accountTypeReady="
+                            + accountTypeReady);
             return;
         }
         PbapClientConnectionHandler connectionHandler = mConnectionHandler;
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapSdpRecord.java b/android/app/src/com/android/bluetooth/pbapclient/PbapSdpRecord.java
new file mode 100644
index 0000000..c475bfc
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapSdpRecord.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.SdpPseRecord;
+
+import java.util.Objects;
+
+/**
+ * This object represents an SDP Record for the PBAP profile. It extends the framework class by
+ * housing all the supported feature masks.
+ */
+public class PbapSdpRecord {
+    public static final int VERSION_1_0 = 0x0100;
+    public static final int VERSION_1_1 = 0x0101;
+    public static final int VERSION_1_2 = 0x0102;
+
+    public static final int FEATURES_EXCLUDED = -1;
+    public static final int FEATURE_DOWNLOADING = 1 << 0;
+    public static final int FEATURE_BROWSING = 1 << 1;
+    public static final int FEATURE_DATABASE_IDENTIFIER = 1 << 2;
+    public static final int FEATURE_FOLDER_VERSION_COUNTERS = 1 << 3;
+    public static final int FEATURE_VCARD_SELECTING = 1 << 4;
+    public static final int FEATURE_ENHANCED_MISSED_CALLS = 1 << 5;
+    public static final int FEATURE_XBT_UCI_VCARD_PROPERTY = 1 << 6;
+    public static final int FEATURE_XBT_UID_VCARD_PROPERTY = 1 << 7;
+    public static final int FEATURE_CONTACT_REFERENCING = 1 << 8;
+    public static final int FEATURE_DEFAULT_IMAGE_FORMAT = 1 << 9;
+
+    // PBAP v1.2.3 Sec. 7.1.2
+    public static final int REPOSITORY_LOCAL_PHONEBOOK = 1 << 0;
+    public static final int REPOSITORY_SIM_CARD = 1 << 1;
+    public static final int REPOSITORY_SPEED_DIAL = 1 << 2;
+    public static final int REPOSITORY_FAVORITES = 1 << 3;
+
+    public static final int FIELD_MISSING = -1;
+
+    private final BluetoothDevice mDevice;
+    private final SdpPseRecord mSdpRecord;
+
+    PbapSdpRecord(BluetoothDevice device, SdpPseRecord record) {
+        mDevice = Objects.requireNonNull(device);
+        mSdpRecord = Objects.requireNonNull(record);
+    }
+
+    /** Get the device associated with this SDP record */
+    public BluetoothDevice getDevice() {
+        return mDevice;
+    }
+
+    /** Get the profile version associated with this SDP record */
+    public int getProfileVersion() {
+        return mSdpRecord.getProfileVersion();
+    }
+
+    /** Get the service name associated with this SDP record */
+    public String getServiceName() {
+        return mSdpRecord.getServiceName();
+    }
+
+    /** Get the L2CAP PSM associated with this SDP record */
+    public int getL2capPsm() {
+        return mSdpRecord.getL2capPsm();
+    }
+
+    /** Get the RFCOMM channel number associated with this SDP record */
+    public int getRfcommChannelNumber() {
+        return mSdpRecord.getRfcommChannelNumber();
+    }
+
+    /** Get the supported features associated with this SDP record */
+    public int getSupportedFeatures() {
+        return mSdpRecord.getSupportedFeatures();
+    }
+
+    /** Returns true if this SDP record supports a given feature */
+    public boolean isFeatureSupported(int feature) {
+        int remoteFeatures = mSdpRecord.getSupportedFeatures();
+        if (remoteFeatures != FIELD_MISSING) {
+            return (feature & remoteFeatures) != 0;
+        }
+        return false;
+    }
+
+    /** Git the supported repositories bitmask associated with this SDP record */
+    public int getSupportedRepositories() {
+        return mSdpRecord.getSupportedRepositories();
+    }
+
+    /** Returns true if this SDP record supports a given repository */
+    public boolean isRepositorySupported(int repository) {
+        int remoteRepositories = mSdpRecord.getSupportedRepositories();
+        if (remoteRepositories != FIELD_MISSING) {
+            return (repository & remoteRepositories) != 0;
+        }
+        return false;
+    }
+
+    /** Get a string representation of this SDP record */
+    @Override
+    public String toString() {
+        return mSdpRecord.toString();
+    }
+
+    /**
+     * Get a string representation of any of the SDP PBAP version constants
+     *
+     * <p>Version is represented as a series of specification defined constants, in the form:
+     * 0x[Major 2 bytes][Minor 2 bytes] -> [Major].[Minor]
+     *
+     * <p>For example, 0x0102 is 1.2.
+     */
+    public static String versionToString(int version) {
+        switch (version) {
+            case FIELD_MISSING:
+                return "VERSION_UNKNOWN";
+            case VERSION_1_0:
+                return "VERSION_1_0";
+            case VERSION_1_1:
+                return "VERSION_1_1";
+            case VERSION_1_2:
+                return "VERSION_1_2";
+            default:
+                return "VERSION_UNRECOGNIZED_" + String.format("%04X", version);
+        }
+    }
+
+    /** Get a string representation of any of the SDP feature constants */
+    public static String featureToString(int feature) {
+        switch (feature) {
+            case FEATURE_DOWNLOADING:
+                return "FEATURE_DOWNLOADING";
+            case FEATURE_BROWSING:
+                return "FEATURE_BROWSING";
+            case FEATURE_DATABASE_IDENTIFIER:
+                return "FEATURE_DATABASE_IDENTIFIER";
+            case FEATURE_FOLDER_VERSION_COUNTERS:
+                return "FEATURE_FOLDER_VERSION_COUNTERS";
+            case FEATURE_VCARD_SELECTING:
+                return "FEATURE_VCARD_SELECTING";
+            case FEATURE_ENHANCED_MISSED_CALLS:
+                return "FEATURE_ENHANCED_MISSED_CALLS";
+            case FEATURE_XBT_UCI_VCARD_PROPERTY:
+                return "FEATURE_XBT_UCI_VCARD_PROPERTY";
+            case FEATURE_XBT_UID_VCARD_PROPERTY:
+                return "FEATURE_XBT_UID_VCARD_PROPERTY";
+            case FEATURE_CONTACT_REFERENCING:
+                return "FEATURE_CONTACT_REFERENCING";
+            case FEATURE_DEFAULT_IMAGE_FORMAT:
+                return "FEATURE_DEFAULT_IMAGE_FORMAT";
+            default:
+                return "FEATURE_RESERVED_BIT_" + feature;
+        }
+    }
+
+    /** Get a string representation of any of the SDP repository constants */
+    public static String repositoryToString(int repository) {
+        switch (repository) {
+            case REPOSITORY_LOCAL_PHONEBOOK:
+                return "REPOSITORY_LOCAL_PHONEBOOK";
+            case REPOSITORY_SIM_CARD:
+                return "REPOSITORY_SIM_CARD";
+            case REPOSITORY_SPEED_DIAL:
+                return "REPOSITORY_SPEED_DIAL";
+            case REPOSITORY_FAVORITES:
+                return "REPOSITORY_FAVORITES";
+            default:
+                return "REPOSITORY_RESERVED_BIT_" + repository;
+        }
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/CallLogPullRequest.java b/android/app/src/com/android/bluetooth/pbapclient/obex/CallLogPullRequest.java
similarity index 95%
rename from android/app/src/com/android/bluetooth/pbapclient/CallLogPullRequest.java
rename to android/app/src/com/android/bluetooth/pbapclient/obex/CallLogPullRequest.java
index 4780d1d..5becf2a 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/CallLogPullRequest.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/CallLogPullRequest.java
@@ -41,7 +41,7 @@
 import java.util.Map;
 
 public class CallLogPullRequest extends PullRequest {
-    private static final String TAG = "CallLogPullRequest";
+    private static final String TAG = CallLogPullRequest.class.getSimpleName();
 
     @VisibleForTesting static final String TIMESTAMP_PROPERTY = "X-IRMC-CALL-DATETIME";
     private static final String TIMESTAMP_FORMAT = "yyyyMMdd'T'HHmmss";
@@ -69,11 +69,11 @@
         Log.d(TAG, "onPullComplete with " + mEntries.size() + " entries");
         int type;
         try {
-            if (path.equals(PbapClientConnectionHandler.ICH_PATH)) {
+            if (path.equals(PbapPhonebook.ICH_PATH)) {
                 type = CallLog.Calls.INCOMING_TYPE;
-            } else if (path.equals(PbapClientConnectionHandler.OCH_PATH)) {
+            } else if (path.equals(PbapPhonebook.OCH_PATH)) {
                 type = CallLog.Calls.OUTGOING_TYPE;
-            } else if (path.equals(PbapClientConnectionHandler.MCH_PATH)) {
+            } else if (path.equals(PbapPhonebook.MCH_PATH)) {
                 type = CallLog.Calls.MISSED_TYPE;
             } else {
                 Log.w(TAG, "Unknown path type:" + path);
diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapApplicationParameters.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapApplicationParameters.java
new file mode 100644
index 0000000..da4fa0e
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapApplicationParameters.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+public class PbapApplicationParameters {
+    private static final String TAG = PbapApplicationParameters.class.getSimpleName();
+
+    // Max size for a phonebook, which determines the max size of a batch and an offset. This comes
+    // from the fact that each field is 2 bytes -> 16 bits -> [0, 65535] (i.e, inclusive)
+    public static final int MAX_PHONEBOOK_SIZE = 65535;
+
+    // Application Parameter Header keys (PBAP 1.2.3, Section 6.2.1)
+    public static final byte OAP_ORDER = 0x01;
+    public static final byte OAP_SEARCH_VALUE = 0x02;
+    public static final byte OAP_SEARCH_PROPERTY = 0x03;
+    public static final byte OAP_MAX_LIST_COUNT = 0x04;
+    public static final byte OAP_LIST_START_OFFSET = 0x05;
+    public static final byte OAP_PROPERTY_SELECTOR = 0x06;
+    public static final byte OAP_FORMAT = 0x07;
+    public static final byte OAP_PHONEBOOK_SIZE = 0x08;
+    public static final byte OAP_NEW_MISSED_CALLS = 0x09;
+    public static final byte OAP_PRIMARY_FOLDER_VERSION = 0x0A;
+    public static final byte OAP_SECONDARY_FOLDER_VERSION = 0x0B;
+    public static final byte OAP_VCARD_SELECTOR = 0x0C;
+    public static final byte OAP_DATABASE_IDENTIFIER = 0x0D;
+    public static final byte OAP_VCARD_SELECTOR_OPERATOR = 0x0E;
+    public static final byte OAP_RESET_NEW_MISSED_CALLS = 0x0F;
+    public static final byte OAP_PBAP_SUPPORTED_FEATURES = 0x10;
+
+    // Property Selector "filter" constants, PBAP 1.2.3, section 5.1.4.1
+    public static final long PROPERTIES_ALL = 0;
+    public static final long PROPERTY_VERSION = 1 << 0;
+    public static final long PROPERTY_FN = 1 << 1;
+    public static final long PROPERTY_N = 1 << 2;
+    public static final long PROPERTY_PHOTO = 1 << 3;
+    public static final long PROPERTY_ADR = 1 << 5;
+    public static final long PROPERTY_TEL = 1 << 7;
+    public static final long PROPERTY_EMAIL = 1 << 8;
+    public static final long PROPERTY_NICKNAME = 1 << 23;
+
+    // MaxListCount field, PBAP 1.2.3, Section 5.3.4.4: "0" signifies to the PSE that the PCE is
+    // requesting the number of indexes in the phonebook of interest that are actually used
+    // (i.e. indexes that correspond to non-NULL entries). Using this causes other parameters to be
+    // ignored. Only metadata will be returned, like the size.
+    public static final int RETURN_SIZE_ONLY = 0;
+
+    private final long mProperties; // 64-bit property selector bit field, 8 bytes
+    private final byte mFormat; // Vcard format, 0 or 1, 1 byte, From PbapVcardList object
+    private final int mMaxListCount; // The total number of items to fetch, for batching, 2 bytes
+    private final int mListStartOffset; // The item index to start at, for batching, 2 bytes
+
+    PbapApplicationParameters(long properties, byte format, int maxListCount, int listStartOffset) {
+        if (maxListCount < 0 || maxListCount > MAX_PHONEBOOK_SIZE) {
+            throw new IllegalArgumentException("maxListCount should be [0, 65535]");
+        }
+        if (listStartOffset < 0 || listStartOffset > MAX_PHONEBOOK_SIZE) {
+            throw new IllegalArgumentException("listStartOffset should be [0, 65535]");
+        }
+        if (format != PbapPhonebook.FORMAT_VCARD_21 && format != PbapPhonebook.FORMAT_VCARD_30) {
+            throw new IllegalArgumentException("VCard format must be 2.1 or 3.0");
+        }
+
+        mProperties = properties;
+        mFormat = format;
+        mMaxListCount = maxListCount;
+        mListStartOffset = listStartOffset;
+    }
+
+    public long getPropertySelectorMask() {
+        return mProperties;
+    }
+
+    public byte getVcardFormat() {
+        return mFormat;
+    }
+
+    public int getMaxListCount() {
+        return mMaxListCount;
+    }
+
+    public int getListStartOffset() {
+        return mListStartOffset;
+    }
+
+    public static String propertiesToString(long properties) {
+        if (properties == PROPERTIES_ALL) {
+            return "PROPERTIES_ALL";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        if ((properties & PROPERTY_VERSION) != 0) {
+            sb.append("PROPERTY_VERSION ");
+        }
+        if ((properties & PROPERTY_FN) != 0) {
+            sb.append("PROPERTY_FN ");
+        }
+        if ((properties & PROPERTY_N) != 0) {
+            sb.append("PROPERTY_N ");
+        }
+        if ((properties & PROPERTY_PHOTO) != 0) {
+            sb.append("PROPERTY_PHOTO ");
+        }
+        if ((properties & PROPERTY_ADR) != 0) {
+            sb.append("PROPERTY_ADR ");
+        }
+        if ((properties & PROPERTY_TEL) != 0) {
+            sb.append("PROPERTY_TEL ");
+        }
+        if ((properties & PROPERTY_EMAIL) != 0) {
+            sb.append("PROPERTY_EMAIL ");
+        }
+        if ((properties & PROPERTY_NICKNAME) != 0) {
+            sb.append("PROPERTY_NICKNAME ");
+        }
+        sb.deleteCharAt(sb.length() - 1);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        return "<"
+                + TAG
+                + (" properties=" + propertiesToString(getPropertySelectorMask()))
+                + (" format=" + getVcardFormat())
+                + (" maxListCount=" + getMaxListCount())
+                + (" listStartOffset=" + getListStartOffset())
+                + ">";
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientConnectionHandler.java
similarity index 72%
rename from android/app/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
rename to android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientConnectionHandler.java
index 470d906..45440c6 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientConnectionHandler.java
@@ -16,13 +16,10 @@
 package com.android.bluetooth.pbapclient;
 
 import android.accounts.Account;
-import android.accounts.AccountManager;
 import android.annotation.SuppressLint;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothSocket;
 import android.bluetooth.BluetoothUuid;
-import android.bluetooth.SdpPseRecord;
-import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -57,14 +54,13 @@
     // progress when Bluetooth stack is torn down.
     private static final int DEFAULT_BATCH_SIZE = 250;
 
-    // Upper limit on the indices of the vcf cards/entries, inclusive,
-    // i.e., valid indices are [0, 1, ... , UPPER_LIMIT]
-    private static final int UPPER_LIMIT = 65535;
-
     static final int MSG_CONNECT = 1;
     static final int MSG_DISCONNECT = 2;
     static final int MSG_DOWNLOAD = 3;
 
+    static final int L2CAP_INVALID_PSM = -1;
+    static final int RFCOMM_INVALID_CHANNEL_ID = -1;
+
     // The following constants are pulled from the Bluetooth Phone Book Access Profile specification
     // 1.1
     private static final byte[] PBAP_TARGET =
@@ -87,84 +83,42 @@
                 0x66
             };
 
-    private static final int PBAP_FEATURE_DEFAULT_IMAGE_FORMAT = 0x00000200;
-    private static final int PBAP_FEATURE_DOWNLOADING = 0x00000001;
-
-    private static final long PBAP_FILTER_VERSION = 1 << 0;
-    private static final long PBAP_FILTER_FN = 1 << 1;
-    private static final long PBAP_FILTER_N = 1 << 2;
-    private static final long PBAP_FILTER_PHOTO = 1 << 3;
-    private static final long PBAP_FILTER_ADR = 1 << 5;
-    private static final long PBAP_FILTER_TEL = 1 << 7;
-    private static final long PBAP_FILTER_EMAIL = 1 << 8;
-    private static final long PBAP_FILTER_NICKNAME = 1 << 23;
-
-    private static final int PBAP_SUPPORTED_FEATURE =
-            PBAP_FEATURE_DEFAULT_IMAGE_FORMAT | PBAP_FEATURE_DOWNLOADING;
-    private static final long PBAP_REQUESTED_FIELDS =
-            PBAP_FILTER_VERSION
-                    | PBAP_FILTER_FN
-                    | PBAP_FILTER_N
-                    | PBAP_FILTER_PHOTO
-                    | PBAP_FILTER_ADR
-                    | PBAP_FILTER_EMAIL
-                    | PBAP_FILTER_TEL
-                    | PBAP_FILTER_NICKNAME;
-
-    @VisibleForTesting static final int L2CAP_INVALID_PSM = -1;
-
-    public static final String PB_PATH = "telecom/pb.vcf";
-    public static final String FAV_PATH = "telecom/fav.vcf";
-    public static final String MCH_PATH = "telecom/mch.vcf";
-    public static final String ICH_PATH = "telecom/ich.vcf";
-    public static final String OCH_PATH = "telecom/och.vcf";
-    public static final String SIM_PB_PATH = "SIM1/telecom/pb.vcf";
-    public static final String SIM_MCH_PATH = "SIM1/telecom/mch.vcf";
-    public static final String SIM_ICH_PATH = "SIM1/telecom/ich.vcf";
-    public static final String SIM_OCH_PATH = "SIM1/telecom/och.vcf";
-
-    // PBAP v1.2.3 Sec. 7.1.2
-    private static final int SUPPORTED_REPOSITORIES_LOCALPHONEBOOK = 1 << 0;
-    private static final int SUPPORTED_REPOSITORIES_SIMCARD = 1 << 1;
-    private static final int SUPPORTED_REPOSITORIES_FAVORITES = 1 << 3;
-
-    public static final int PBAP_V1_2 = 0x0102;
-    public static final byte VCARD_TYPE_21 = 0;
-    public static final byte VCARD_TYPE_30 = 1;
-
     private Account mAccount;
-    private AccountManager mAccountManager;
     private BluetoothSocket mSocket;
     private final BluetoothDevice mDevice;
+    private final int mLocalSupportedFeatures;
     // PSE SDP Record for current device.
-    private SdpPseRecord mPseRec = null;
+    private PbapSdpRecord mPseRec = null;
     private ClientSession mObexSession;
-    private Context mContext;
-    private BluetoothPbapObexAuthenticator mAuth = null;
+    private PbapClientService mService;
+    private PbapClientObexAuthenticator mAuth = null;
     private final PbapClientStateMachine mPbapClientStateMachine;
     private boolean mAccountCreated;
 
     /**
      * Constructs PCEConnectionHandler object
      *
-     * @param pceHandlerbuild To build BluetoothPbapClientHandler Instance.
+     * @param pceHandlerbuild To build PbapClientConnectionHandler Instance.
      */
     PbapClientConnectionHandler(Builder pceHandlerbuild) {
         super(pceHandlerbuild.mLooper);
         mDevice = pceHandlerbuild.mDevice;
-        mContext = pceHandlerbuild.mContext;
+        mLocalSupportedFeatures = pceHandlerbuild.mLocalSupportedFeatures;
+        mService = pceHandlerbuild.mService;
         mPbapClientStateMachine = pceHandlerbuild.mClientStateMachine;
-        mAuth = new BluetoothPbapObexAuthenticator();
-        mAccountManager = AccountManager.get(mPbapClientStateMachine.getContext());
+        mAuth = new PbapClientObexAuthenticator();
         mAccount =
-                new Account(mDevice.getAddress(), mContext.getString(R.string.pbap_account_type));
+                new Account(
+                        mDevice.getAddress(),
+                        mService.getString(R.string.pbap_client_account_type));
     }
 
     public static class Builder {
 
         private Looper mLooper;
-        private Context mContext;
+        private PbapClientService mService;
         private BluetoothDevice mDevice;
+        private int mLocalSupportedFeatures;
         private PbapClientStateMachine mClientStateMachine;
 
         public Builder setLooper(Looper loop) {
@@ -172,6 +126,11 @@
             return this;
         }
 
+        public Builder setLocalSupportedFeatures(int features) {
+            this.mLocalSupportedFeatures = features;
+            return this;
+        }
+
         public Builder setClientSM(PbapClientStateMachine clientStateMachine) {
             this.mClientStateMachine = clientStateMachine;
             return this;
@@ -182,8 +141,8 @@
             return this;
         }
 
-        public Builder setContext(Context context) {
-            this.mContext = context;
+        public Builder setService(PbapClientService service) {
+            this.mService = service;
             return this;
         }
 
@@ -198,7 +157,8 @@
         Log.d(TAG, "Handling Message = " + msg.what);
         switch (msg.what) {
             case MSG_CONNECT:
-                mPseRec = (SdpPseRecord) msg.obj;
+                mPseRec = (PbapSdpRecord) msg.obj;
+
                 /* To establish a connection, first open a socket and then create an OBEX session */
                 if (connectSocket()) {
                     Log.d(TAG, "Socket connected");
@@ -247,20 +207,20 @@
                     Log.e(TAG, "Account creation failed.");
                     return;
                 }
-                if (isRepositorySupported(SUPPORTED_REPOSITORIES_FAVORITES)) {
-                    downloadContacts(FAV_PATH);
+                if (mPseRec.isRepositorySupported(PbapSdpRecord.REPOSITORY_FAVORITES)) {
+                    downloadContacts(PbapPhonebook.FAVORITES_PATH);
                 }
-                if (isRepositorySupported(SUPPORTED_REPOSITORIES_LOCALPHONEBOOK)) {
-                    downloadContacts(PB_PATH);
+                if (mPseRec.isRepositorySupported(PbapSdpRecord.REPOSITORY_LOCAL_PHONEBOOK)) {
+                    downloadContacts(PbapPhonebook.LOCAL_PHONEBOOK_PATH);
                 }
-                if (isRepositorySupported(SUPPORTED_REPOSITORIES_SIMCARD)) {
-                    downloadContacts(SIM_PB_PATH);
+                if (mPseRec.isRepositorySupported(PbapSdpRecord.REPOSITORY_SIM_CARD)) {
+                    downloadContacts(PbapPhonebook.SIM_PHONEBOOK_PATH);
                 }
 
                 Map<String, Integer> callCounter = new HashMap<>();
-                downloadCallLog(MCH_PATH, callCounter);
-                downloadCallLog(ICH_PATH, callCounter);
-                downloadCallLog(OCH_PATH, callCounter);
+                downloadCallLog(PbapPhonebook.MCH_PATH, callCounter);
+                downloadCallLog(PbapPhonebook.ICH_PATH, callCounter);
+                downloadCallLog(PbapPhonebook.OCH_PATH, callCounter);
                 break;
 
             default:
@@ -269,7 +229,7 @@
     }
 
     @VisibleForTesting
-    synchronized void setPseRecord(SdpPseRecord record) {
+    synchronized void setPseRecord(PbapSdpRecord record) {
         mPseRec = record;
     }
 
@@ -293,9 +253,12 @@
             } else if (mPseRec.getL2capPsm() != L2CAP_INVALID_PSM) {
                 Log.v(TAG, "connectSocket: PSM: " + mPseRec.getL2capPsm());
                 mSocket = mDevice.createL2capSocket(mPseRec.getL2capPsm());
-            } else {
+            } else if (mPseRec.getRfcommChannelNumber() != RFCOMM_INVALID_CHANNEL_ID) {
                 Log.v(TAG, "connectSocket: channel: " + mPseRec.getRfcommChannelNumber());
                 mSocket = mDevice.createRfcommSocket(mPseRec.getRfcommChannelNumber());
+            } else {
+                Log.w(TAG, "connectSocket: transport PSM or channel ID not specified");
+                return false;
             }
 
             if (mSocket != null) {
@@ -330,10 +293,10 @@
 
                 ObexAppParameters oap = new ObexAppParameters();
 
-                if (mPseRec.getProfileVersion() >= PBAP_V1_2) {
+                if (mPseRec.getProfileVersion() >= PbapSdpRecord.VERSION_1_2) {
                     oap.add(
-                            BluetoothPbapRequest.OAP_TAGID_PBAP_SUPPORTED_FEATURES,
-                            PBAP_SUPPORTED_FEATURE);
+                            PbapApplicationParameters.OAP_PBAP_SUPPORTED_FEATURES,
+                            mLocalSupportedFeatures);
                 }
 
                 oap.addToHeaderSet(connectionRequest);
@@ -379,14 +342,21 @@
             PhonebookPullRequest processor =
                     new PhonebookPullRequest(mPbapClientStateMachine.getContext());
 
+            PbapApplicationParameters params =
+                    new PbapApplicationParameters(
+                            PbapApplicationParameters.PROPERTIES_ALL,
+                            /* format, unused */ (byte) 0,
+                            PbapApplicationParameters.RETURN_SIZE_ONLY,
+                            /* list start offeset, start from beginning */ 0);
+
             // Download contacts in batches of size DEFAULT_BATCH_SIZE
-            BluetoothPbapRequestPullPhoneBookSize requestPbSize =
-                    new BluetoothPbapRequestPullPhoneBookSize(path, PBAP_REQUESTED_FIELDS);
+            RequestPullPhonebookMetadata requestPbSize =
+                    new RequestPullPhonebookMetadata(path, params);
             requestPbSize.execute(mObexSession);
 
-            int numberOfContactsRemaining = requestPbSize.getSize();
+            int numberOfContactsRemaining = requestPbSize.getMetadata().getSize();
             int startOffset = 0;
-            if (PB_PATH.equals(path)) {
+            if (PbapPhonebook.LOCAL_PHONEBOOK_PATH.equals(path)) {
                 // PBAP v1.2.3, Sec 3.1.5. The first contact in pb is owner card 0.vcf, which we
                 // do not want to download. The other phonebook objects (e.g., fav) don't have an
                 // owner card, so they don't need an offset.
@@ -395,22 +365,24 @@
                 numberOfContactsRemaining -= 1;
             }
 
-            while ((numberOfContactsRemaining > 0) && (startOffset <= UPPER_LIMIT)) {
+            while ((numberOfContactsRemaining > 0)
+                    && (startOffset <= PbapApplicationParameters.MAX_PHONEBOOK_SIZE)) {
                 int numberOfContactsToDownload =
                         Math.min(
                                 Math.min(DEFAULT_BATCH_SIZE, numberOfContactsRemaining),
-                                UPPER_LIMIT - startOffset + 1);
-                BluetoothPbapRequestPullPhoneBook request =
-                        new BluetoothPbapRequestPullPhoneBook(
-                                path,
-                                mAccount,
-                                PBAP_REQUESTED_FIELDS,
-                                VCARD_TYPE_30,
+                                PbapApplicationParameters.MAX_PHONEBOOK_SIZE - startOffset + 1);
+
+                params =
+                        new PbapApplicationParameters(
+                                PbapApplicationParameters.PROPERTIES_ALL,
+                                PbapPhonebook.FORMAT_VCARD_30,
                                 numberOfContactsToDownload,
                                 startOffset);
+
+                RequestPullPhonebook request = new RequestPullPhonebook(path, params, mAccount);
                 request.execute(mObexSession);
                 List<VCardEntry> vcards = request.getList();
-                if (FAV_PATH.equals(path)) {
+                if (PbapPhonebook.FAVORITES_PATH.equals(path)) {
                     // mark each vcard as a favorite
                     for (VCardEntry v : vcards) {
                         v.setStarred(true);
@@ -422,7 +394,8 @@
                 startOffset += numberOfContactsToDownload;
                 numberOfContactsRemaining -= numberOfContactsToDownload;
             }
-            if ((startOffset > UPPER_LIMIT) && (numberOfContactsRemaining > 0)) {
+            if ((startOffset > PbapApplicationParameters.MAX_PHONEBOOK_SIZE)
+                    && (numberOfContactsRemaining > 0)) {
                 Log.w(TAG, "Download contacts incomplete, index exceeded upper limit.");
             }
         } catch (IOException e) {
@@ -435,8 +408,14 @@
     @VisibleForTesting
     void downloadCallLog(String path, Map<String, Integer> callCounter) {
         try {
-            BluetoothPbapRequestPullPhoneBook request =
-                    new BluetoothPbapRequestPullPhoneBook(path, mAccount, 0, VCARD_TYPE_30, 0, 0);
+            PbapApplicationParameters params =
+                    new PbapApplicationParameters(
+                            /* properties, unused for call logs */ 0,
+                            PbapPhonebook.FORMAT_VCARD_30,
+                            0,
+                            0);
+
+            RequestPullPhonebook request = new RequestPullPhonebook(path, params, mAccount);
             request.execute(mObexSession);
             CallLogPullRequest processor =
                     new CallLogPullRequest(
@@ -452,16 +431,18 @@
 
     @VisibleForTesting
     boolean addAccount() {
-        if (mAccountManager.addAccountExplicitly(mAccount, null, null)) {
+        if (mService.addAccount(mAccount)) {
             Log.d(TAG, "Added account " + mAccount);
             return true;
+        } else {
+            Log.e(TAG, "Failed to add account " + mAccount);
         }
         return false;
     }
 
     @VisibleForTesting
     void removeAccount() {
-        if (mAccountManager.removeAccountExplicitly(mAccount)) {
+        if (mService.removeAccount(mAccount)) {
             Log.d(TAG, "Removed account " + mAccount);
         } else {
             Log.e(TAG, "Failed to remove account " + mAccount);
@@ -472,11 +453,11 @@
     void removeCallLog() {
         try {
             // need to check call table is exist ?
-            if (mContext.getContentResolver() == null) {
+            if (mService.getContentResolver() == null) {
                 Log.d(TAG, "CallLog ContentResolver is not found");
                 return;
             }
-            mContext.getContentResolver()
+            mService.getContentResolver()
                     .delete(
                             CallLog.Calls.CONTENT_URI,
                             Calls.PHONE_ACCOUNT_ID + "=?",
@@ -485,13 +466,4 @@
             Log.d(TAG, "Call Logs could not be deleted, they may not exist yet.");
         }
     }
-
-    @VisibleForTesting
-    boolean isRepositorySupported(int mask) {
-        if (mPseRec == null) {
-            Log.v(TAG, "No PBAP Server SDP Record");
-            return false;
-        }
-        return (mask & mPseRec.getSupportedRepositories()) != 0;
-    }
 }
diff --git a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticator.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexAuthenticator.java
similarity index 92%
rename from android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticator.java
rename to android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexAuthenticator.java
index a062efb..733dc23 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticator.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexAuthenticator.java
@@ -28,8 +28,8 @@
  * with PSE devices prior to PBAP 1.2. With profiles prior to 1.2 the actual initiation of
  * authentication is implementation defined.
  */
-class BluetoothPbapObexAuthenticator implements Authenticator {
-    private static final String TAG = "PbapClientObexAuth";
+class PbapClientObexAuthenticator implements Authenticator {
+    private static final String TAG = PbapClientObexAuthenticator.class.getSimpleName();
 
     // Default session key for legacy devices is 0000
     @VisibleForTesting String mSessionKey = "0000";
diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientRequest.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientRequest.java
new file mode 100644
index 0000000..fd9d0f6
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientRequest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.util.Log;
+
+import com.android.obex.ClientOperation;
+import com.android.obex.ClientSession;
+import com.android.obex.HeaderSet;
+import com.android.obex.ResponseCodes;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+abstract class PbapClientRequest {
+    static final String TAG = PbapClientRequest.class.getSimpleName();
+
+    // Request Types
+    public static final int TYPE_PULL_PHONEBOOK_METADATA = 0;
+    public static final int TYPE_PULL_PHONEBOOK = 1;
+
+    protected HeaderSet mHeaderSet = new HeaderSet();
+    private int mResponseCode = -1;
+
+    PbapClientRequest() {
+        mResponseCode = -1;
+    }
+
+    /**
+     * A function that returns the type of the request.
+     *
+     * <p>Used to determine type instead of using 'instanceof'
+     */
+    public abstract int getType();
+
+    /**
+     * Get the actual response code associated with the request
+     *
+     * @return The response code as in integer
+     */
+    public final int getResponseCode() {
+        return mResponseCode;
+    }
+
+    /**
+     * A generica operation, providing overridable hooks to read response headers and content.
+     *
+     * <p>All PBAP Client operations are GET OBEX operations, so that is what this is.
+     */
+    public void execute(ClientSession session) throws IOException {
+        Log.v(TAG, "execute");
+        ClientOperation operation = null;
+        try {
+            operation = (ClientOperation) session.get(mHeaderSet);
+
+            /* make sure final flag for GET is used (PBAP spec 6.2.2) */
+            operation.setGetFinalFlag(true);
+
+            /*
+             * this will trigger ClientOperation to use non-buffered stream so
+             * we can abort operation
+             */
+            operation.continueOperation(true, false);
+
+            readResponseHeaders(operation.getReceivedHeader());
+            InputStream inputStream = operation.openInputStream();
+            readResponse(inputStream);
+            inputStream.close();
+            mResponseCode = operation.getResponseCode();
+        } catch (IOException e) {
+            mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+            Log.e(TAG, "IOException occurred when processing request", e);
+            throw e;
+        } finally {
+            // Always close the operation so the next operation can successfully complete
+            if (operation != null) {
+                operation.close();
+            }
+        }
+    }
+
+    protected void readResponse(InputStream stream) throws IOException {
+        Log.v(TAG, "readResponse");
+        /* nothing here by default */
+    }
+
+    protected void readResponseHeaders(HeaderSet headerset) {
+        Log.v(TAG, "readResponseHeaders");
+        /* nothing here by default */
+    }
+
+    public static String typeToString(int type) {
+        switch (type) {
+            case TYPE_PULL_PHONEBOOK_METADATA:
+                return "TYPE_PULL_PHONEBOOK_METADATA";
+            case TYPE_PULL_PHONEBOOK:
+                return "TYPE_PULL_PHONEBOOK";
+            default:
+                return "TYPE_RESERVED (" + type + ")";
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "<"
+                + TAG
+                + (" type=" + typeToString(getType()))
+                + (", responseCode=" + getResponseCode())
+                + ">";
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapPhonebook.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapPhonebook.java
new file mode 100644
index 0000000..a7bc5b6
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapPhonebook.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import com.android.vcard.VCardConfig;
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardEntryConstructor;
+import com.android.vcard.VCardEntryHandler;
+import com.android.vcard.VCardParser;
+import com.android.vcard.VCardParser_V21;
+import com.android.vcard.VCardParser_V30;
+import com.android.vcard.exception.VCardException;
+import com.android.vcard.exception.VCardVersionException;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PbapPhonebook {
+    private static final String TAG = PbapPhonebook.class.getSimpleName();
+
+    // {@link BufferedInputStream#DEFAULT_BUFFER_SIZE} is not public
+    private static final int BIS_DEFAULT_BUFFER_SIZE = 8192;
+
+    // Phonebooks, including call history. See PBAP 1.2.3, Section 3.1.2
+    public static final String LOCAL_PHONEBOOK_PATH = "telecom/pb.vcf"; // Device phonebook
+    public static final String FAVORITES_PATH = "telecom/fav.vcf"; // Contacts marked as favorite
+    public static final String MCH_PATH = "telecom/mch.vcf"; // Missed Calls
+    public static final String ICH_PATH = "telecom/ich.vcf"; // Incoming Calls
+    public static final String OCH_PATH = "telecom/och.vcf"; // Outgoing Calls
+    public static final String SIM_PHONEBOOK_PATH = "SIM1/telecom/pb.vcf"; // SIM stored phonebook
+    public static final String SIM_MCH_PATH = "SIM1/telecom/mch.vcf"; // SIM stored Missed Calls
+    public static final String SIM_ICH_PATH = "SIM1/telecom/ich.vcf"; // SIM stored Incoming Calls
+    public static final String SIM_OCH_PATH = "SIM1/telecom/och.vcf"; // SIM stored Outgoing Calls
+
+    // VCard Formats, both are required to be supported by the Server, PBAP 1.2.3, Section 5.1.4.2
+    public static byte FORMAT_VCARD_21 = 0;
+    public static byte FORMAT_VCARD_30 = 1;
+
+    private final String mPhonebook;
+    private final int mListStartOffset;
+    private final List<VCardEntry> mCards = new ArrayList<VCardEntry>();
+
+    // Needed for VCard parsing, since the account on older platform versions cannot be associated
+    // with the VCard (to construct a query) after parse time. Newer platform versions support this
+    // though, which means we can eventually remove this in favor of assigning an account post parse
+    // time.
+    @Nullable private final Account mAccount;
+
+    class CardEntryHandler implements VCardEntryHandler {
+        @Override
+        public void onStart() {}
+
+        @Override
+        public void onEntryCreated(VCardEntry entry) {
+            mCards.add(entry);
+        }
+
+        @Override
+        public void onEnd() {}
+    }
+
+    PbapPhonebook(String phonebook) {
+        mPhonebook = phonebook;
+        mAccount = null;
+        mListStartOffset = 0;
+    }
+
+    PbapPhonebook(
+            String phonebook,
+            byte format,
+            int listStartOffset,
+            @Nullable Account account,
+            InputStream inputStream)
+            throws IOException {
+        if (format != FORMAT_VCARD_21 && format != FORMAT_VCARD_30) {
+            throw new IllegalArgumentException("Unsupported vCard version.");
+        }
+        mPhonebook = phonebook;
+        mListStartOffset = listStartOffset;
+        mAccount = account;
+        parse(inputStream, format);
+    }
+
+    private void parse(InputStream in, byte format) throws IOException {
+        VCardParser parser;
+
+        if (format == FORMAT_VCARD_30) {
+            parser = new VCardParser_V30();
+        } else {
+            parser = new VCardParser_V21();
+        }
+
+        VCardEntryConstructor constructor =
+                new VCardEntryConstructor(VCardConfig.VCARD_TYPE_V21_GENERIC, mAccount);
+
+        CardEntryHandler handler = new CardEntryHandler();
+        constructor.addEntryHandler(handler);
+
+        parser.addInterpreter(constructor);
+
+        // {@link BufferedInputStream} supports the {@link InputStream#mark} and
+        // {@link InputStream#reset} methods.
+        BufferedInputStream bufferedInput = new BufferedInputStream(in);
+        bufferedInput.mark(BIS_DEFAULT_BUFFER_SIZE /* readlimit */);
+
+        // If there is a {@link VCardVersionException}, try parsing again with a different
+        // version. Otherwise, parsing either succeeds (i.e., no {@link VCardException}) or it
+        // fails with a different {@link VCardException}.
+        if (parsedWithVcardVersionException(parser, bufferedInput)) {
+            // PBAP v1.2.3 only supports vCard versions 2.1 and 3.0; it's one or the other
+            if (format == FORMAT_VCARD_21) {
+                parser = new VCardParser_V30();
+                Log.w(TAG, "vCard version and Parser mismatch; expected v2.1, switching to v3.0");
+            } else {
+                parser = new VCardParser_V21();
+                Log.w(TAG, "vCard version and Parser mismatch; expected v3.0, switching to v2.1");
+            }
+            // reset and try again
+            bufferedInput.reset();
+            mCards.clear();
+            constructor.clear();
+            parser.addInterpreter(constructor);
+            if (parsedWithVcardVersionException(parser, bufferedInput)) {
+                Log.e(TAG, "unsupported vCard version, neither v2.1 nor v3.0");
+            }
+        }
+    }
+
+    /**
+     * Attempts to parse, with an eye on whether the correct version of Parser is used.
+     *
+     * @param parser -- the {@link VCardParser} to use.
+     * @param in -- the {@link InputStream} to parse.
+     * @return {@code true} if there was a {@link VCardVersionException}; {@code false} if there is
+     *     any other {@link VCardException} or succeeds (i.e., no {@link VCardException}).
+     * @throws IOException if there's an issue reading the {@link InputStream}.
+     */
+    private boolean parsedWithVcardVersionException(VCardParser parser, InputStream in)
+            throws IOException {
+        try {
+            parser.parse(in);
+        } catch (VCardVersionException e1) {
+            Log.w(TAG, "vCard version and Parser mismatch", e1);
+            return true;
+        } catch (VCardException e2) {
+            Log.e(TAG, "vCard exception", e2);
+        }
+        return false;
+    }
+
+    /**
+     * Get the phonebook path associated with this PbapPhonebook object
+     *
+     * @return a string representing the path these VCard objects were requested from
+     */
+    public String getPhonebook() {
+        return mPhonebook;
+    }
+
+    /**
+     * Get the offset associated with this PbapPhonebook object
+     *
+     * <p>The offset respresents the start index of the remote contacts pull
+     *
+     * @return an int representing the offset index where this pull started from
+     */
+    public int getOffset() {
+        return mListStartOffset;
+    }
+
+    /**
+     * Get the total number of contacts contained in this phonebook
+     *
+     * @return an int containing the total number of contacts contained in this phonebook
+     */
+    public int getCount() {
+        return mCards.size();
+    }
+
+    /**
+     * Get the list of VCard objects contained in this phonebook
+     *
+     * @return a list of VCard objects in this phonebook
+     */
+    public List<VCardEntry> getList() {
+        return mCards;
+    }
+
+    @Override
+    public String toString() {
+        return "<" + TAG + "phonebook=" + mPhonebook + " entries=" + getCount() + ">";
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapPhonebookMetadata.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapPhonebookMetadata.java
new file mode 100644
index 0000000..3badee4
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapPhonebookMetadata.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+public class PbapPhonebookMetadata {
+    private static final String TAG = PbapPhonebookMetadata.class.getSimpleName();
+
+    public static final int INVALID_SIZE = -1;
+    public static final String INVALID_DATABASE_IDENTIFIER = null;
+    public static final String DEFAULT_DATABASE_IDENTIFIER = "0";
+    public static final String INVALID_VERSION_COUNTER = null;
+
+    private final String mPhonebook;
+    private int mSize = INVALID_SIZE; // 2 byte number
+    private String mDatabaseIdentifier = INVALID_DATABASE_IDENTIFIER; // 16 byte number as string
+    private String mPrimaryVersionCounter = INVALID_VERSION_COUNTER; // 16 byte number as string
+    private String mSecondaryVersionCounter = INVALID_VERSION_COUNTER; // 16 byte number as string
+
+    PbapPhonebookMetadata(
+            String phonebook,
+            int size,
+            String databaseIdentifier,
+            String primaryVersionCounter,
+            String secondaryVersionCounter) {
+        mPhonebook = phonebook;
+        mSize = size;
+        mDatabaseIdentifier = databaseIdentifier;
+        mPrimaryVersionCounter = primaryVersionCounter;
+        mSecondaryVersionCounter = secondaryVersionCounter;
+    }
+
+    public String getPhonebook() {
+        return mPhonebook;
+    }
+
+    public int getSize() {
+        return mSize;
+    }
+
+    public String getDatabaseIdentifier() {
+        return mDatabaseIdentifier;
+    }
+
+    public String getPrimaryVersionCounter() {
+        return mPrimaryVersionCounter;
+    }
+
+    public String getSecondaryVersionCounter() {
+        return mSecondaryVersionCounter;
+    }
+
+    @Override
+    public String toString() {
+        return "<"
+                + TAG
+                + (" phonebook=" + mPhonebook)
+                + (" size=" + mSize)
+                + (" databaseIdentifier=" + mDatabaseIdentifier)
+                + (" primaryVersionCounter=" + mPrimaryVersionCounter)
+                + (" secondaryVersionCounter=" + mSecondaryVersionCounter)
+                + ">";
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PhonebookPullRequest.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PhonebookPullRequest.java
similarity index 98%
rename from android/app/src/com/android/bluetooth/pbapclient/PhonebookPullRequest.java
rename to android/app/src/com/android/bluetooth/pbapclient/obex/PhonebookPullRequest.java
index a0780af..dc1fd92 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PhonebookPullRequest.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PhonebookPullRequest.java
@@ -38,7 +38,7 @@
 
     public PhonebookPullRequest(Context context) {
         mContext = context;
-        path = PbapClientConnectionHandler.PB_PATH;
+        path = PbapPhonebook.LOCAL_PHONEBOOK_PATH;
     }
 
     @Override
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PullRequest.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PullRequest.java
similarity index 82%
rename from android/app/src/com/android/bluetooth/pbapclient/PullRequest.java
rename to android/app/src/com/android/bluetooth/pbapclient/obex/PullRequest.java
index f938107..210518e 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PullRequest.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PullRequest.java
@@ -23,6 +23,12 @@
     public String path;
     protected List<VCardEntry> mEntries;
 
+    /**
+     * How this request should be handled and the data should be stored once its complete.
+     *
+     * <p>Override to provide an implementation specific to the type of results your implementation
+     * expects.
+     */
     public abstract void onPullComplete();
 
     @Override
diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/RequestPullPhonebook.java b/android/app/src/com/android/bluetooth/pbapclient/obex/RequestPullPhonebook.java
new file mode 100644
index 0000000..e889b27
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/RequestPullPhonebook.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.accounts.Account;
+
+import com.android.bluetooth.ObexAppParameters;
+import com.android.obex.HeaderSet;
+import com.android.vcard.VCardEntry;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+final class RequestPullPhonebook extends PbapClientRequest {
+    private static final String TAG = RequestPullPhonebook.class.getSimpleName();
+
+    private static final String TYPE = "x-bt/phonebook";
+
+    private final String mPhonebook;
+    private final byte mFormat;
+    private final int mMaxListCount;
+    private final int mListStartOffset;
+    private Account mAccount;
+
+    private PbapPhonebook mResponse;
+
+    @Override
+    public int getType() {
+        return TYPE_PULL_PHONEBOOK;
+    }
+
+    RequestPullPhonebook(String phonebook, PbapApplicationParameters params, Account account) {
+        mPhonebook = phonebook;
+        mFormat = params.getVcardFormat();
+        mMaxListCount = params.getMaxListCount();
+        mListStartOffset = params.getListStartOffset();
+        mAccount = account;
+
+        long properties = params.getPropertySelectorMask();
+
+        mHeaderSet.setHeader(HeaderSet.NAME, phonebook);
+        mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
+
+        ObexAppParameters oap = new ObexAppParameters();
+
+        oap.add(PbapApplicationParameters.OAP_FORMAT, mFormat);
+
+        if (properties != 0) {
+            oap.add(PbapApplicationParameters.OAP_PROPERTY_SELECTOR, properties);
+        }
+
+        if (mListStartOffset > 0) {
+            oap.add(PbapApplicationParameters.OAP_LIST_START_OFFSET, (short) mListStartOffset);
+        }
+
+        // maxListCount == 0 indicates to fetch all, in which case we set it to the upper bound
+        // Note that Java has no unsigned types. To capture an unsigned value in the range [0, 2^16)
+        // we need to use an int and cast to a short (2 bytes). This packs the bits we want.
+        if (mMaxListCount > 0) {
+            oap.add(PbapApplicationParameters.OAP_MAX_LIST_COUNT, (short) mMaxListCount);
+        } else {
+            oap.add(
+                    PbapApplicationParameters.OAP_MAX_LIST_COUNT,
+                    (short) PbapApplicationParameters.MAX_PHONEBOOK_SIZE);
+        }
+
+        oap.addToHeaderSet(mHeaderSet);
+    }
+
+    @Override
+    protected void readResponse(InputStream stream) throws IOException {
+        mResponse = new PbapPhonebook(mPhonebook, mFormat, mListStartOffset, mAccount, stream);
+    }
+
+    public String getPhonebook() {
+        return mPhonebook;
+    }
+
+    public List<VCardEntry> getList() {
+        return mResponse.getList();
+    }
+
+    public PbapPhonebook getContacts() {
+        return mResponse;
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/RequestPullPhonebookMetadata.java b/android/app/src/com/android/bluetooth/pbapclient/obex/RequestPullPhonebookMetadata.java
new file mode 100644
index 0000000..f6ce3fe
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/RequestPullPhonebookMetadata.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import com.android.bluetooth.ObexAppParameters;
+import com.android.obex.HeaderSet;
+
+/**
+ * This implements a PullPhonebook request, with the goal of only fetching metadata for the given
+ * phonebook, but not the actual phonebook contents itself.
+ *
+ * <p>This is done by requesting the phonebook, but omitting the MaxListCount parameter, signaling
+ * that we're not interested in the contents (PBAP 1.2.3, Section 5.1, C7 of the Response Format
+ * table)
+ */
+final class RequestPullPhonebookMetadata extends PbapClientRequest {
+    private static final String TAG = RequestPullPhonebookMetadata.class.getSimpleName();
+
+    private static final String TYPE = "x-bt/phonebook";
+
+    private final String mPhonebook;
+    private PbapPhonebookMetadata mResponse;
+
+    @Override
+    public int getType() {
+        return TYPE_PULL_PHONEBOOK_METADATA;
+    }
+
+    RequestPullPhonebookMetadata(String phonebook, PbapApplicationParameters params) {
+        mPhonebook = phonebook;
+        mHeaderSet.setHeader(HeaderSet.NAME, phonebook);
+        mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
+
+        // Set MaxListCount in the request to 0 to get PhonebookSize in the response.
+        // If a vCardSelector is present in the request, then the result shall
+        // contain the number of items that satisfy the selector’s criteria.
+        // See PBAP v1.2.3, Sec. 5.1.4.5.
+        ObexAppParameters oap = new ObexAppParameters();
+        oap.add(PbapApplicationParameters.OAP_MAX_LIST_COUNT, (short) 0);
+
+        // Otherwise, listen to the property selector criteria passed in and ignore the rest
+        long properties = params.getPropertySelectorMask();
+        if (properties != PbapApplicationParameters.PROPERTIES_ALL) {
+            oap.add(PbapApplicationParameters.OAP_PROPERTY_SELECTOR, properties);
+        }
+        oap.addToHeaderSet(mHeaderSet);
+    }
+
+    @Override
+    protected void readResponseHeaders(HeaderSet headerset) {
+        int size = PbapPhonebookMetadata.INVALID_SIZE;
+        String databaseIdentifier = PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER;
+        String primaryVersionCounter = PbapPhonebookMetadata.INVALID_VERSION_COUNTER;
+        String secondaryVersionCounter = PbapPhonebookMetadata.INVALID_VERSION_COUNTER;
+
+        ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
+        if (oap.exists(PbapApplicationParameters.OAP_PHONEBOOK_SIZE)) {
+            size = oap.getShort(PbapApplicationParameters.OAP_PHONEBOOK_SIZE);
+        }
+
+        mResponse =
+                new PbapPhonebookMetadata(
+                        mPhonebook,
+                        size,
+                        databaseIdentifier,
+                        primaryVersionCounter,
+                        secondaryVersionCounter);
+    }
+
+    public String getPhonebook() {
+        return mPhonebook;
+    }
+
+    public PbapPhonebookMetadata getMetadata() {
+        return mResponse;
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
index 6bd8682..0299cc8 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
@@ -43,6 +43,7 @@
 import android.media.AudioDevicePort;
 import android.media.AudioManager;
 import android.os.test.TestLooper;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
@@ -385,10 +386,13 @@
     @Test
     public void headsetRemoveActive_fallbackToLeAudio() {
         when(mHeadsetService.getFallbackDevice()).thenReturn(mHeadsetDevice);
+        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
+
+        InOrder order = inOrder(mLeAudioService);
 
         leAudioConnected(mLeAudioDevice);
         mTestLooper.dispatchAll();
-        verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice);
+        order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice);
 
         headsetConnected(mHeadsetDevice, false);
         mTestLooper.dispatchAll();
@@ -827,7 +831,28 @@
 
     /** One LE Audio is connected and disconnected later. Should then set active device to null. */
     @Test
+    @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER)
     public void lastLeAudioDisconnected_clearLeAudioActive() {
+        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
+        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);
+
+        leAudioConnected(mLeAudioDevice);
+        mTestLooper.dispatchAll();
+        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
+
+        leAudioDisconnected(mLeAudioDevice);
+        mTestLooper.dispatchAll();
+        verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
+        verify(mLeAudioService).deviceDisconnected(mLeAudioDevice, false);
+    }
+
+    /** One LE Audio is connected and disconnected later. Should then set active device to null. */
+    @Test
+    @DisableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER)
+    public void lastLeAudioDisconnected_clearLeAudioActive_NoFixDisconnectFlag() {
+        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
+        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);
+
         leAudioConnected(mLeAudioDevice);
         mTestLooper.dispatchAll();
         verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
@@ -857,11 +882,16 @@
     }
 
     /**
-     * Two LE Audio are connected and the current active is then disconnected. Should then set
-     * active device to fallback device.
+     * Two LE Audio Sets are connected and the current active Set is disconnected. The other
+     * connected LeAudio Set shall become an active device.
      */
     @Test
     public void leAudioSecondDeviceDisconnected_fallbackDeviceActive() {
+        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
+        when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(2);
+        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);
+        when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice2);
+
         leAudioConnected(mLeAudioDevice);
         mTestLooper.dispatchAll();
         verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
@@ -1101,6 +1131,7 @@
     @Test
     public void leAudioAndA2dpConnectedThenA2dpDisconnected_fallbackToLeAudio() {
         when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
+        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
 
         leAudioConnected(mLeAudioDevice);
         mTestLooper.dispatchAll();
@@ -1143,13 +1174,13 @@
     }
 
     /**
-     * An LE Audio set connected. The active bud disconnected. Set active device returns false
-     * indicating an issue (the other bud is also disconnected). Then the active device should be
-     * removed and hasFallback should be set to false.
+     * An LE Audio set connected. The active bud disconnected. Active device manager should not
+     * choose other set member as active device.
      */
     @Test
     public void leAudioSetConnectedThenActiveOneDisconnected_noFallback() {
         when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
+        when(mLeAudioService.getLeadDevice(any())).thenReturn(mLeAudioDevice);
 
         leAudioConnected(mLeAudioDevice);
         mTestLooper.dispatchAll();
@@ -1157,71 +1188,76 @@
 
         leAudioConnected(mLeAudioDevice2);
         mTestLooper.dispatchAll();
-        verify(mLeAudioService).setActiveDevice(mLeAudioDevice2);
-
-        Mockito.clearInvocations(mLeAudioService);
-
-        // Return false to indicate an issue when setting new active device
-        // (e.g. the other device disconnected as well).
-        when(mLeAudioService.setActiveDevice(any())).thenReturn(false);
+        verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice2);
 
         leAudioDisconnected(mLeAudioDevice2);
         mTestLooper.dispatchAll();
-        verify(mLeAudioService).removeActiveDevice(false);
+        verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
         verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, false);
     }
 
-    /**
-     * An LE Audio set connected. The active bud disconnected. Set active device returns true
-     * indicating the other bud is going to be the active device. Then the active device should
-     * change and hasFallback should be set to true.
-     */
     @Test
-    public void leAudioSetConnectedThenActiveOneDisconnected_hasFallback() {
-        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
-
-        leAudioConnected(mLeAudioDevice);
-        mTestLooper.dispatchAll();
-        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
-
-        leAudioConnected(mLeAudioDevice2);
-        mTestLooper.dispatchAll();
-        verify(mLeAudioService).setActiveDevice(mLeAudioDevice2);
-
-        Mockito.clearInvocations(mLeAudioService);
-
-        leAudioDisconnected(mLeAudioDevice2);
-        mTestLooper.dispatchAll();
-        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
-        verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, true);
-    }
-
-    @Test
+    @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER)
     public void leAudioSetConnectedGroupThenDisconnected_noFallback() {
         when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
 
+        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
+        when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(1);
+        when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice);
+        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);
+
+        InOrder order = inOrder(mLeAudioService);
+
         leAudioConnected(mLeAudioDevice);
         mTestLooper.dispatchAll();
-        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
+        order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
 
-        Mockito.clearInvocations(mLeAudioService);
-
-        when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice);
         leAudioConnected(mLeAudioDevice2);
         mTestLooper.dispatchAll();
-        verify(mLeAudioService, never()).setActiveDevice(any());
-
-        Mockito.clearInvocations(mLeAudioService);
+        order.verify(mLeAudioService, never()).setActiveDevice(any());
 
         leAudioDisconnected(mLeAudioDevice2);
         mTestLooper.dispatchAll();
-        verify(mLeAudioService, never()).setActiveDevice(any());
-        verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
+        order.verify(mLeAudioService, never()).setActiveDevice(any());
+        order.verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
+        order.verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, false);
 
         leAudioDisconnected(mLeAudioDevice);
         mTestLooper.dispatchAll();
-        verify(mLeAudioService).removeActiveDevice(false);
-        verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, false);
+        order.verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
+        order.verify(mLeAudioService).deviceDisconnected(mLeAudioDevice, false);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER)
+    public void leAudioSetConnectedGroupThenDisconnected_noFallback_NoFixDisconnectFlag() {
+        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
+
+        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
+        when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(1);
+        when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice);
+        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);
+
+        InOrder order = inOrder(mLeAudioService);
+
+        leAudioConnected(mLeAudioDevice);
+        mTestLooper.dispatchAll();
+        order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
+
+        leAudioConnected(mLeAudioDevice2);
+        mTestLooper.dispatchAll();
+        order.verify(mLeAudioService, never()).setActiveDevice(any());
+
+        leAudioDisconnected(mLeAudioDevice2);
+        mTestLooper.dispatchAll();
+        order.verify(mLeAudioService, never()).setActiveDevice(any());
+        order.verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
+        order.verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, false);
+
+        leAudioDisconnected(mLeAudioDevice);
+        mTestLooper.dispatchAll();
+        order.verify(mLeAudioService).removeActiveDevice(false);
+        order.verify(mLeAudioService).deviceDisconnected(mLeAudioDevice, false);
     }
 
     /**
@@ -1255,6 +1291,11 @@
     @Test
     @EnableFlags(Flags.FLAG_ADM_VERIFY_ACTIVE_FALLBACK_DEVICE)
     public void sameDeviceAsAshaAndLeAudio_noFallbackOnSwitch() {
+        /* Dual mode ASHA/LeAudio device from group 1 */
+        when(mLeAudioService.getGroupId(mHearingAidDevice)).thenReturn(1);
+        /* Different LeAudio only device from group 2 */
+        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(2);
+
         when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
 
         /* Connect first device as ASHA */
@@ -1262,11 +1303,12 @@
         mTestLooper.dispatchAll();
         verify(mHearingAidService).setActiveDevice(mHearingAidDevice);
 
-        /* Connect first device as LE Audio */
-        leAudioConnected(mHearingAidDevice);
+        /* Disconnect ASHA and connect first device as LE Audio */
         hearingAidDisconnected(mHearingAidDevice);
         mTestLooper.dispatchAll();
-        verify(mHearingAidService).removeActiveDevice(false);
+        verify(mHearingAidService).removeActiveDevice(true /* stop audio */);
+        leAudioConnected(mHearingAidDevice);
+        mTestLooper.dispatchAll();
         verify(mLeAudioService).setActiveDevice(mHearingAidDevice);
 
         /* Connect second device as LE Audio. First device is disconnected with fallback to
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
index d216f66..8738085 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
@@ -708,7 +708,6 @@
 
     @Test
     public void restrictedHandles() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_GATT_CLEANUP_RESTRICTED_HANDLES);
         int clientIf = 1;
         int connId = 1;
         ArrayList<GattDbElement> db = new ArrayList<>();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
index 1ca22be..2ee7fa0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
@@ -56,6 +56,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.test.TestLooper;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.Pair;
 
@@ -67,6 +68,7 @@
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.RemoteDevices;
+import com.android.bluetooth.flags.Flags;
 import com.android.bluetooth.hfp.HeadsetService;
 
 import org.hamcrest.Matcher;
@@ -133,6 +135,8 @@
         doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices();
         doReturn(true).when(mNativeInterface).sendAndroidAt(anyObject(), anyString());
 
+        doReturn(true).when(mNativeInterface).disconnect(any(BluetoothDevice.class));
+
         mTestLooper = new TestLooper();
         mHeadsetClientStateMachine =
                 new TestHeadsetClientStateMachine(
@@ -980,7 +984,8 @@
         assertName(HeadsetClientStateMachine.QUERY_OPERATOR_NAME, "QUERY_OPERATOR_NAME");
         assertName(HeadsetClientStateMachine.SUBSCRIBER_INFO, "SUBSCRIBER_INFO");
         assertName(HeadsetClientStateMachine.CONNECTING_TIMEOUT, "CONNECTING_TIMEOUT");
-        int unknownMessageInt = 54;
+        assertName(HeadsetClientStateMachine.DISCONNECTING_TIMEOUT, "DISCONNECTING_TIMEOUT");
+        int unknownMessageInt = 55;
         assertName(unknownMessageInt, "UNKNOWN(" + unknownMessageInt + ")");
     }
 
@@ -1232,6 +1237,27 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testConnectedState_ProcessDisconnectMessage_TransitionToDisconnecting() {
+        initToDisconnectingState();
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testProcessStackEvent_ConnectionStateChanged_Disconnected_onConnectedState() {
+        initToConnectedState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED;
+        event.device = mTestDevice;
+        sendMessage(StackEvent.STACK_EVENT, event);
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnected.class);
+        verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false));
+    }
+
+    @Test
     public void testProcessConnectAudioMessage_onConnectedState() {
         initToConnectedState();
         sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
@@ -1269,7 +1295,7 @@
     @Test
     public void testProcessDisconnectAudioMessage_onAudioOnState() {
         initToAudioOnState();
-        sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO, mTestDevice);
+        sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
         verify(mNativeInterface).disconnectAudio(any(BluetoothDevice.class));
     }
 
@@ -1282,7 +1308,7 @@
         mHeadsetClientStateMachine.mCalls.put(0, call);
         int[] states = new int[1];
         states[0] = HfpClientCall.CALL_STATE_ACTIVE;
-        sendMessage(HeadsetClientStateMachine.HOLD_CALL, mTestDevice);
+        sendMessage(HeadsetClientStateMachine.HOLD_CALL);
         verify(mNativeInterface).handleCallAction(any(BluetoothDevice.class), anyInt(), eq(0));
     }
 
@@ -1340,6 +1366,132 @@
         assertThat(mHeadsetClientStateMachine.mAudioSWB).isTrue();
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testDisconnectingState_TransitionToDisconnected() {
+        initToDisconnectingState();
+
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED;
+        event.device = mTestDevice;
+
+        sendMessage(StackEvent.STACK_EVENT, event);
+        mTestLooper.dispatchAll();
+        verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_DISCONNECTED));
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnected.class);
+
+        verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testDisconnectingState_ReceiveConnectMsg_DeferMessage() {
+        // case CONNECT:
+        initToDisconnectingState();
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.CONNECT))
+                .isFalse();
+        sendMessage(HeadsetClientStateMachine.CONNECT);
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.CONNECT))
+                .isTrue();
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testDisconnectingState_ReceiveConnectAudioMsg_DeferMessage() {
+        // case CONNECT_AUDIO:
+        initToDisconnectingState();
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.CONNECT_AUDIO))
+                .isFalse();
+        sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.CONNECT_AUDIO))
+                .isTrue();
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testDisconnectingState_ReceiveDisconnectMsg_DeferMessage() {
+        // case DISCONNECT:
+        initToDisconnectingState();
+        sendMessage(HeadsetClientStateMachine.DISCONNECT);
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.DISCONNECT))
+                .isTrue();
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testDisconnectingState_ReceiveDisconnectAudioMsg_DeferMessage() {
+        // case DISCONNECT_AUDIO:
+        initToDisconnectingState();
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.DISCONNECT_AUDIO))
+                .isFalse();
+        sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.DISCONNECT_AUDIO))
+                .isTrue();
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testDisconnectingState_ReceiveUnknownMsg_NotHandled() {
+        initToDisconnectingState();
+        sendMessage(HeadsetClientStateMachine.NO_ACTION);
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testAudioOnState_ReceiveDisconnectMsg_DeferMessage() {
+        initToAudioOnState();
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.DISCONNECT))
+                .isFalse();
+        sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice);
+        assertThat(
+                        mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                                HeadsetClientStateMachine.DISCONNECT))
+                .isTrue();
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.AudioOn.class);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE)
+    public void testDisconnectingState_DisconnectingTimeout_TransitionToDisconnected() {
+        initToDisconnectingState();
+        // Trigger timeout
+        mTestLooper.moveTimeForward(HeadsetClientStateMachine.DISCONNECTING_TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+        verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_DISCONNECTED));
+        assertThat(mHeadsetClientStateMachine.getCurrentState())
+                .isInstanceOf(HeadsetClientStateMachine.Disconnected.class);
+
+        verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false));
+    }
+
     /**
      * Allow/disallow connection to any device
      *
@@ -1380,6 +1532,14 @@
                 .isInstanceOf(HeadsetClientStateMachine.AudioOn.class);
     }
 
+    private void initToDisconnectingState() {
+        initToConnectedState();
+        sendMessageAndVerifyTransition(
+                mHeadsetClientStateMachine.obtainMessage(
+                        HeadsetClientStateMachine.DISCONNECT, mTestDevice),
+                HeadsetClientStateMachine.Disconnecting.class);
+    }
+
     private void verifySendBroadcastMultiplePermissions(Matcher<Intent>... matchers) {
         mInOrder.verify(mHeadsetClientService)
                 .sendBroadcastMultiplePermissions(
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
index 1543cd6..ac6fccb 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
@@ -35,6 +35,7 @@
 import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 
@@ -66,6 +67,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeoutException;
@@ -1564,6 +1566,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION)
     public void testUpdateFallbackInputDevice() {
         mSetFlagsRule.disableFlags(Flags.FLAG_LEAUDIO_USE_AUDIO_MODE_LISTENER);
         int groupId = 1;
@@ -1658,6 +1661,49 @@
         }
     }
 
+    @Test
+    @EnableFlags({
+        Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION,
+        Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP,
+        Flags.FLAG_LEAUDIO_USE_AUDIO_MODE_LISTENER
+    })
+    public void testManageBroadcastToUnicastFallbackGroup() {
+        int groupId = 1;
+        int groupId2 = 2;
+        int broadcastId = 243;
+        byte[] code = {0x00, 0x01, 0x00, 0x02};
+        List<BluetoothDevice> devices = new ArrayList<>();
+
+        when(mDatabaseManager.getMostRecentlyConnectedDevices()).thenReturn(devices);
+
+        initializeNative();
+        devices.add(mDevice);
+        prepareHandoverStreamingBroadcast(groupId, broadcastId, code);
+        mService.deviceConnected(mDevice);
+        devices.add(mDevice2);
+        prepareConnectedUnicastDevice(groupId2, mDevice2);
+        mService.deviceConnected(mDevice2);
+
+        Assert.assertEquals(mService.mUnicastGroupIdDeactivatedForBroadcastTransition, groupId);
+
+        reset(mAudioManager);
+
+        /* Update fallback active device (only input is active) */
+        ArgumentCaptor<BluetoothProfileConnectionInfo> connectionInfoArgumentCaptor =
+                ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class);
+
+        mService.setBroadcastToUnicastFallbackGroup(groupId2);
+
+        verify(mAudioManager)
+                .handleBluetoothActiveDeviceChanged(
+                        eq(mDevice2), eq(mDevice), connectionInfoArgumentCaptor.capture());
+        List<BluetoothProfileConnectionInfo> connInfos =
+                connectionInfoArgumentCaptor.getAllValues();
+        Assert.assertEquals(connInfos.size(), 1);
+        Assert.assertFalse(connInfos.get(0).isLeOutput());
+        Assert.assertEquals(mService.getBroadcastToUnicastFallbackGroup(), groupId2);
+    }
+
     private BluetoothLeBroadcastSettings buildBroadcastSettingsFromMetadata(
             BluetoothLeAudioContentMetadata contentMetadata,
             @Nullable byte[] broadcastCode,
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
index 2cd1f1a..076e4dc 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
@@ -56,6 +56,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.sysprop.BluetoothProperties;
@@ -1826,6 +1827,7 @@
 
     /** Test update unicast fallback active group when broadcast is ongoing */
     @Test
+    @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION)
     public void testUpdateUnicastFallbackActiveDeviceGroupDuringBroadcast() {
         int groupId = 1;
         int preGroupId = 2;
@@ -3639,4 +3641,82 @@
                 .setGroupAllowedContextMask(
                         groupId, BluetoothLeAudio.CONTEXTS_ALL, BluetoothLeAudio.CONTEXTS_ALL);
     }
+
+    /** Test managing broadcast to unicast fallback group */
+    @Test
+    @EnableFlags({
+        Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION,
+        Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP
+    })
+    public void testManageBroadcastToUnicastFallbackGroup() {
+        int firstGroupId = 1;
+        int secondGroupId = 2;
+        /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
+        int direction = 1;
+        int snkAudioLocation = 3;
+        int srcAudioLocation = 4;
+        int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
+        List<BluetoothDevice> devices = new ArrayList<>();
+
+        when(mDatabaseManager.getMostRecentlyConnectedDevices()).thenReturn(devices);
+
+        // Not connected device
+        assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
+        assertThat(mService.getBroadcastToUnicastFallbackGroup())
+                .isEqualTo(BluetoothLeAudio.GROUP_ID_INVALID);
+
+        // Connected device
+        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+        devices.add(mSingleDevice);
+        connectTestDevice(mSingleDevice, testGroupId);
+
+        // Group should be updated to default (earliest connected)
+        assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(firstGroupId);
+
+        // Add location support
+        LeAudioStackEvent audioConfChangedEvent =
+                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
+        audioConfChangedEvent.device = mSingleDevice;
+        audioConfChangedEvent.valueInt1 = direction;
+        audioConfChangedEvent.valueInt2 = firstGroupId;
+        audioConfChangedEvent.valueInt3 = snkAudioLocation;
+        audioConfChangedEvent.valueInt4 = srcAudioLocation;
+        audioConfChangedEvent.valueInt5 = availableContexts;
+        mService.messageFromNative(audioConfChangedEvent);
+
+        assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
+        verify(mNativeInterface).groupSetActive(firstGroupId);
+
+        // Set group and device as active
+        LeAudioStackEvent groupStatusChangedEvent =
+                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
+        groupStatusChangedEvent.valueInt1 = firstGroupId;
+        groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
+        mService.messageFromNative(groupStatusChangedEvent);
+
+        // Set fallback group to not valid (not connected)
+        mService.setBroadcastToUnicastFallbackGroup(secondGroupId);
+
+        // Connect second device
+        devices.add(mLeftDevice);
+        connectTestDevice(mLeftDevice, secondGroupId);
+        mService.deviceConnected(mLeftDevice);
+
+        // Fallback device should remain earliest connected
+        assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(firstGroupId);
+
+        // Set fallback group to valid second
+        mService.setBroadcastToUnicastFallbackGroup(secondGroupId);
+
+        // Fallback device should be changed to second
+        assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(secondGroupId);
+
+        // no active device
+        assertThat(mService.removeActiveDevice(false)).isTrue();
+        verify(mNativeInterface).groupSetActive(BluetoothLeAudio.GROUP_ID_INVALID);
+
+        // Set group and device as inactive active
+        groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE;
+        mService.messageFromNative(groupStatusChangedEvent);
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java
index 1563d21..16cc355 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java
@@ -76,7 +76,7 @@
     @Before
     public void setUp() throws IOException {
         mFakeMapObexServer = new FakeMapObexServer();
-        mFakeClientSession = new ClientSession(mFakeMapObexServer.mClientObexTransport);
+        mFakeClientSession = mFakeMapObexServer.getClientSession();
         mFakeClientSession.connect(new HeaderSet());
     }
 
@@ -182,7 +182,7 @@
 
         @Override
         @SuppressWarnings("JavaUtilDate")
-        public int onGetValidator(final Operation op) {
+        public int onGet(final Operation op) {
             OutputStream outputStream;
             HeaderSet replyHeaders = new HeaderSet();
             BluetoothMapAppParams outAppParams = new BluetoothMapAppParams();
@@ -223,7 +223,7 @@
         }
 
         @Override
-        public int onPutValidator(final Operation op) {
+        public int onPut(final Operation op) {
             try {
                 HeaderSet request = op.getReceivedHeader();
                 String type = (String) request.getHeader(HeaderSet.TYPE);
@@ -263,7 +263,7 @@
         }
 
         @Override
-        public int onSetPathValidator(
+        public int onSetPath(
                 final HeaderSet request,
                 HeaderSet reply,
                 final boolean backup,
diff --git a/android/app/tests/unit/src/com/android/bluetooth/obex/FakeObexServer.java b/android/app/tests/unit/src/com/android/bluetooth/obex/FakeObexServer.java
index 85beb8e..7b7183b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/obex/FakeObexServer.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/obex/FakeObexServer.java
@@ -16,6 +16,7 @@
 
 package com.android.bluetooth;
 
+import com.android.obex.ClientSession;
 import com.android.obex.HeaderSet;
 import com.android.obex.ObexTransport;
 import com.android.obex.Operation;
@@ -23,61 +24,266 @@
 import com.android.obex.ServerRequestHandler;
 import com.android.obex.ServerSession;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
 
-/**
- * A fake obex server for testing obex clients. Test cases should implement *Validator functions to
- * validate input and return appropriate responses for individual tests.
- *
- * <p>Note: it is important to not perform any testing Assert operations within the validators as
- * that would crash the testing framework.
- */
-public abstract class FakeObexServer {
+/** A Fake OBEX Server base class for use when testing OBEX clients and client requests. */
+public class FakeObexServer {
+    private static final String TAG = FakeObexServer.class.getSimpleName();
 
-    public ObexTransport mClientObexTransport;
+    // Server streams to talk with client under test
+    private final PipedInputStream mReceiveFromClient;
+    private final PipedOutputStream mSendToClient;
 
-    private ObexTransport mServerObexTransport;
-    private Server mFakeServer;
-    private FakeObexTransport mFakeObexTransport;
+    // Client streams to talk to server
+    private final PipedInputStream mReceiveFromServer;
+    private final PipedOutputStream mSendToServer;
+
+    // Transports for either side
+    private final TestObexTransport mServerTransport;
+    private final TestObexTransport mClientTransport;
+
+    private final TestServiceRequestHandler mRequestHandler;
+    private final ServerSession mServerSession;
 
     public FakeObexServer() throws IOException {
-        mFakeServer = new Server();
-        mFakeObexTransport = new FakeObexTransport();
-        mServerObexTransport = mFakeObexTransport.mServerTransport;
-        mClientObexTransport = mFakeObexTransport.mClientTransport;
-        new ServerSession(mServerObexTransport, mFakeServer, null);
+
+        mSendToServer = new PipedOutputStream();
+        mReceiveFromServer = new PipedInputStream();
+        mReceiveFromClient = new PipedInputStream(mSendToServer);
+        mSendToClient = new PipedOutputStream(mReceiveFromServer);
+
+        // Transports
+        mServerTransport = new TestObexTransport(mReceiveFromClient, mSendToClient, true);
+        mClientTransport = new TestObexTransport(mReceiveFromServer, mSendToServer, true);
+
+        mRequestHandler = new TestServiceRequestHandler(this);
+        mServerSession = new ServerSession(mServerTransport, mRequestHandler, null);
     }
 
-    public abstract int onGetValidator(Operation op);
+    /**
+     * Get a transport for use with a client.
+     *
+     * <p>You can use the openInputStream() and openOutputStream() to get the underlying stream
+     * objects and inject them into your objects under test.
+     */
+    public ObexTransport getClientTransport() {
+        return mClientTransport;
+    }
 
-    public abstract int onPutValidator(Operation op);
+    /**
+     * Directly create a session with this server.
+     *
+     * <p>This can be used to quickly test request objects that need a ClientSession
+     */
+    public ClientSession getClientSession() throws IOException {
+        return new ClientSession(mClientTransport);
+    }
 
-    public abstract int onSetPathValidator(
-            HeaderSet request, HeaderSet reply, boolean backup, boolean create);
+    /**
+     * This will close the underlying transport, which will close the streams given to us.
+     *
+     * <p>By specification, servers themselves cannot issue an OBEX session level disconnect.
+     */
+    public void close() {
+        mServerSession.close();
+    }
 
-    class Server extends ServerRequestHandler {
+    // *********************************************************************************************
+    // * Server Operations
+    // *********************************************************************************************
 
-        @Override
-        public int onConnect(final HeaderSet request, HeaderSet reply) {
-            return ResponseCodes.OBEX_HTTP_OK;
+    public int onConnect(final HeaderSet request, HeaderSet reply) {
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    public void onDisconnect(final HeaderSet request, HeaderSet reply) {}
+
+    public int onGet(final Operation op) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    public int onPut(final Operation op) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    public int onAbort(final HeaderSet request, HeaderSet reply) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    public int onSetPath(
+            final HeaderSet request, HeaderSet reply, final boolean backup, final boolean create) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    public void onClose() {}
+
+    /**
+     * Send a response to a client with the given headers and payload
+     *
+     * @param op The Operation object representing the ongoing operation with the client
+     * @param replyHeaders The HeaderSet to return to the client
+     * @param bytes The payload to send in the response, if any
+     */
+    public final int sendResponse(Operation op, HeaderSet replyHeaders, byte[] bytes) {
+        int responseCode = ResponseCodes.OBEX_HTTP_OK;
+        OutputStream outStream = null;
+        int maxChunkSize = 0;
+        int bytesToWrite = 0;
+        int bytesWritten = 0;
+
+        try {
+            op.sendHeaders(replyHeaders); // Do this before getting chunk size
+            outStream = op.openOutputStream();
+            if (bytes == null) {
+                op.noBodyHeader();
+                outStream.flush();
+            } else {
+                maxChunkSize = op.getMaxPacketSize();
+                while (bytesWritten < bytes.length) {
+                    bytesToWrite = Math.min(maxChunkSize, bytes.length - bytesWritten);
+                    outStream.write(bytes, bytesWritten, bytesToWrite);
+                    bytesWritten += bytesToWrite;
+                }
+            }
+        } catch (IOException e) {
+            responseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        } finally {
+            // Make sure we close
+            if (outStream != null) {
+                try {
+                    outStream.close();
+                } catch (IOException e) {
+                    // drop, as we're closing anyways
+                }
+            }
+        }
+        // If we didn't write everything then send the error code
+        if (bytes != null && bytesWritten != bytes.length) {
+            responseCode = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+        // Otherwise, success!
+        return responseCode;
+    }
+
+    // *********************************************************************************************
+    // * Transport
+    // *********************************************************************************************
+
+    public static class TestObexTransport implements ObexTransport {
+
+        private final InputStream mInput;
+        private final OutputStream mOutput;
+        private boolean mIsSrmSupported;
+
+        public TestObexTransport(InputStream input, OutputStream output, boolean isSrmSupported) {
+            mInput = input;
+            mOutput = output;
+            setSrmSupported(isSrmSupported);
         }
 
         @Override
-        public void onDisconnect(final HeaderSet request, HeaderSet reply) {}
+        public DataInputStream openDataInputStream() throws IOException {
+            return new DataInputStream(openInputStream());
+        }
+
+        @Override
+        public DataOutputStream openDataOutputStream() throws IOException {
+            return new DataOutputStream(openOutputStream());
+        }
+
+        @Override
+        public InputStream openInputStream() throws IOException {
+            return mInput;
+        }
+
+        @Override
+        public OutputStream openOutputStream() throws IOException {
+            return mOutput;
+        }
+
+        @Override
+        public int getMaxReceivePacketSize() {
+            return -1;
+        }
+
+        @Override
+        public int getMaxTransmitPacketSize() {
+            return -1;
+        }
+
+        @Override
+        public void connect() throws IOException {}
+
+        @Override
+        public void create() throws IOException {}
+
+        @Override
+        public void disconnect() throws IOException {}
+
+        @Override
+        public void listen() throws IOException {}
+
+        @Override
+        public void close() throws IOException {
+            mInput.close();
+            mOutput.close();
+        }
+
+        public boolean isSrmSupported() {
+            return mIsSrmSupported;
+        }
+
+        public void setSrmSupported(boolean isSrmSupported) {
+            mIsSrmSupported = isSrmSupported;
+        }
+    }
+
+    // *********************************************************************************************
+    // * Request Handler
+    // *********************************************************************************************
+
+    /**
+     * Internal ServerRequestHandler that delegates calls to the FakeObexServer implementation
+     *
+     * <p>This is setup this way for easier test syntax, so one can extend the fake without needing
+     * to care about the framework specifics
+     */
+    private static class TestServiceRequestHandler extends ServerRequestHandler {
+        private final FakeObexServer mServer;
+
+        public TestServiceRequestHandler(FakeObexServer server) {
+            mServer = server;
+        }
+
+        @Override
+        public int onConnect(final HeaderSet request, HeaderSet reply) {
+            return mServer.onConnect(request, reply);
+        }
+
+        @Override
+        public void onDisconnect(final HeaderSet request, HeaderSet reply) {
+            mServer.onDisconnect(request, reply);
+        }
 
         @Override
         public int onGet(final Operation op) {
-            return onGetValidator(op);
+            return mServer.onGet(op);
         }
 
         @Override
         public int onPut(final Operation op) {
-            return onPutValidator(op);
+            return mServer.onPut(op);
         }
 
         @Override
         public int onAbort(final HeaderSet request, HeaderSet reply) {
-            return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+            return mServer.onAbort(request, reply);
         }
 
         @Override
@@ -86,10 +292,12 @@
                 HeaderSet reply,
                 final boolean backup,
                 final boolean create) {
-            return onSetPathValidator(request, reply, backup, create);
+            return mServer.onSetPath(request, reply, backup, create);
         }
 
         @Override
-        public void onClose() {}
+        public void onClose() {
+            mServer.onClose();
+        }
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/obex/FakeObexTransport.java b/android/app/tests/unit/src/com/android/bluetooth/obex/FakeObexTransport.java
deleted file mode 100644
index 27e0a29..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/obex/FakeObexTransport.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth;
-
-import com.android.obex.ObexTransport;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-
-/**
- * A fake obex transport used for testing Client/Server connections. The transport uses two pairs of
- * pipes to route input from the client to the server, and back. The obex transport is of the
- * simplest form, returning default values for everything.
- */
-public class FakeObexTransport {
-    ObexTransport mClientTransport;
-    ObexTransport mServerTransport;
-
-    PipedInputStream mClientInputStream;
-    PipedInputStream mServerInputStream;
-    PipedOutputStream mClientOutputStream;
-    PipedOutputStream mServerOutputStream;
-
-    public FakeObexTransport() throws IOException {
-        mClientInputStream = new PipedInputStream();
-        mServerOutputStream = new PipedOutputStream(mClientInputStream);
-        mServerInputStream = new PipedInputStream();
-        mClientOutputStream = new PipedOutputStream(mServerInputStream);
-
-        mClientTransport = new BiDirectionalTransport(mClientInputStream, mClientOutputStream);
-        mServerTransport = new BiDirectionalTransport(mServerInputStream, mServerOutputStream);
-    }
-
-    static class BiDirectionalTransport implements ObexTransport {
-
-        InputStream mInputStream;
-        OutputStream mOutputStream;
-
-        BiDirectionalTransport(InputStream inputStream, OutputStream outputStream) {
-            mInputStream = inputStream;
-            mOutputStream = outputStream;
-        }
-
-        @Override
-        public DataInputStream openDataInputStream() throws IOException {
-            return new DataInputStream(openInputStream());
-        }
-
-        @Override
-        public DataOutputStream openDataOutputStream() throws IOException {
-            return new DataOutputStream(openOutputStream());
-        }
-
-        @Override
-        public InputStream openInputStream() throws IOException {
-            return mInputStream;
-        }
-
-        @Override
-        public OutputStream openOutputStream() throws IOException {
-            return mOutputStream;
-        }
-
-        @Override
-        public void connect() throws IOException {}
-
-        @Override
-        public void create() throws IOException {}
-
-        @Override
-        public void disconnect() throws IOException {}
-
-        @Override
-        public void listen() throws IOException {}
-
-        @Override
-        public void close() throws IOException {}
-
-        public boolean isConnected() throws IOException {
-            return true;
-        }
-
-        @Override
-        public int getMaxTransmitPacketSize() {
-            return -1;
-        }
-
-        @Override
-        public int getMaxReceivePacketSize() {
-            return -1;
-        }
-
-        @Override
-        public boolean isSrmSupported() {
-            return true;
-        }
-    }
-}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSizeTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSizeTest.java
deleted file mode 100644
index 35209b8..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSizeTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.obex.HeaderSet;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BluetoothPbapRequestPullPhoneBookSizeTest {
-
-    BluetoothPbapRequestPullPhoneBookSize mRequest;
-
-    @Before
-    public void setUp() {
-        mRequest =
-                new BluetoothPbapRequestPullPhoneBookSize(
-                        /* pbName= */ "phonebook", /* filter= */ 1);
-    }
-
-    @Test
-    public void readResponseHeaders() {
-        try {
-            HeaderSet headerSet = new HeaderSet();
-            mRequest.readResponseHeaders(headerSet);
-            assertThat(mRequest.getSize()).isEqualTo(0);
-        } catch (Exception e) {
-            assertWithMessage("Exception should not happen.").fail();
-        }
-    }
-}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookTest.java
deleted file mode 100644
index c2ef8ce..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.mock;
-
-import android.accounts.Account;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.obex.HeaderSet;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BluetoothPbapRequestPullPhoneBookTest {
-
-    private static final String PB_NAME = "phonebook";
-    private static final Account ACCOUNT = mock(Account.class);
-
-    @Test
-    public void constructor_wrongMaxListCount_throwsIAE() {
-        final long filter = 0;
-        final byte format = PbapClientConnectionHandler.VCARD_TYPE_30;
-        final int listStartOffset = 10;
-
-        final int wrongMaxListCount = -1;
-
-        assertThrows(
-                IllegalArgumentException.class,
-                () ->
-                        new BluetoothPbapRequestPullPhoneBook(
-                                PB_NAME,
-                                ACCOUNT,
-                                filter,
-                                format,
-                                wrongMaxListCount,
-                                listStartOffset));
-    }
-
-    @Test
-    public void constructor_wrongListStartOffset_throwsIAE() {
-        final long filter = 0;
-        final byte format = PbapClientConnectionHandler.VCARD_TYPE_30;
-        final int maxListCount = 100;
-
-        final int wrongListStartOffset = -1;
-
-        assertThrows(
-                IllegalArgumentException.class,
-                () ->
-                        new BluetoothPbapRequestPullPhoneBook(
-                                PB_NAME,
-                                ACCOUNT,
-                                filter,
-                                format,
-                                maxListCount,
-                                wrongListStartOffset));
-    }
-
-    @Test
-    public void readResponse_failWithInputStreamThatThrowsIOEWhenRead() {
-        final long filter = 1;
-        final byte format = 0; // Will be properly handled as VCARD_TYPE_21.
-        final int maxListCount = 0; // Will be specially handled as 65535.
-        final int listStartOffset = 10;
-        BluetoothPbapRequestPullPhoneBook request =
-                new BluetoothPbapRequestPullPhoneBook(
-                        PB_NAME, ACCOUNT, filter, format, maxListCount, listStartOffset);
-
-        final InputStream is =
-                new InputStream() {
-                    @Override
-                    public int read() throws IOException {
-                        throw new IOException();
-                    }
-
-                    @Override
-                    public int read(byte[] b) throws IOException {
-                        throw new IOException();
-                    }
-
-                    @Override
-                    public int read(byte[] b, int off, int len) throws IOException {
-                        throw new IOException();
-                    }
-                };
-
-        assertThrows(IOException.class, () -> request.readResponse(is));
-    }
-
-    @Test
-    public void readResponseHeaders() {
-        final long filter = 1;
-        final byte format = 0; // Will be properly handled as VCARD_TYPE_21.
-        final int maxListCount = 0; // Will be specially handled as 65535.
-        final int listStartOffset = 10;
-        BluetoothPbapRequestPullPhoneBook request =
-                new BluetoothPbapRequestPullPhoneBook(
-                        PB_NAME, ACCOUNT, filter, format, maxListCount, listStartOffset);
-
-        try {
-            HeaderSet headerSet = new HeaderSet();
-            request.readResponseHeaders(headerSet);
-            assertThat(request.getNewMissedCalls()).isEqualTo(-1);
-        } catch (Exception e) {
-            assertWithMessage("Exception should not happen.").fail();
-        }
-    }
-}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestTest.java
deleted file mode 100644
index 8a5a5d5..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.Mockito.mock;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.obex.ClientSession;
-import com.android.obex.HeaderSet;
-import com.android.obex.ObexTransport;
-import com.android.obex.ResponseCodes;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.io.InputStream;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BluetoothPbapRequestTest {
-
-    private BluetoothPbapRequest mRequest = new BluetoothPbapRequest() {};
-
-    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
-
-    @Mock private ObexTransport mObexTransport;
-
-    @Before
-    public void setUp() throws Exception {
-        mRequest = new BluetoothPbapRequest() {};
-    }
-
-    @Test
-    public void isSuccess_true() {
-        mRequest.mResponseCode = ResponseCodes.OBEX_HTTP_OK;
-
-        assertThat(mRequest.isSuccess()).isTrue();
-    }
-
-    @Test
-    public void isSuccess_false() {
-        mRequest.mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
-
-        assertThat(mRequest.isSuccess()).isFalse();
-    }
-
-    @Test
-    public void execute_afterAbort() throws Exception {
-        mRequest.abort();
-        ClientSession session = new ClientSession(mObexTransport);
-        mRequest.execute(session);
-
-        assertThat(mRequest.mResponseCode).isEqualTo(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR);
-    }
-
-    // TODO: Add execute_success test case.
-
-    @Test
-    public void emptyMethods() {
-        try {
-            mRequest.readResponse(mock(InputStream.class));
-            mRequest.readResponseHeaders(new HeaderSet());
-            mRequest.checkResponseCode(ResponseCodes.OBEX_HTTP_OK);
-
-        } catch (Exception e) {
-            assertWithMessage("Exception should not happen.").fail();
-        }
-    }
-}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardListTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardListTest.java
deleted file mode 100644
index 3882560..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapVcardListTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.mock;
-
-import android.accounts.Account;
-import android.content.Context;
-import android.content.res.Resources;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.bluetooth.TestUtils;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BluetoothPbapVcardListTest {
-
-    private static final Account ACCOUNT = mock(Account.class);
-    private Context mTargetContext = InstrumentationRegistry.getTargetContext();
-    private Resources mTestResources = TestUtils.getTestApplicationResources(mTargetContext);
-
-    @Test
-    public void constructor_withInputStreamThatThrowsIoeWhenRead_throwsIOException() {
-
-        final InputStream is =
-                new InputStream() {
-                    @Override
-                    public int read() throws IOException {
-                        throw new IOException();
-                    }
-
-                    @Override
-                    public int read(byte[] b) throws IOException {
-                        throw new IOException();
-                    }
-
-                    @Override
-                    public int read(byte[] b, int off, int len) throws IOException {
-                        throw new IOException();
-                    }
-                };
-
-        assertThrows(
-                IOException.class,
-                () ->
-                        new BluetoothPbapVcardList(
-                                ACCOUNT, is, PbapClientConnectionHandler.VCARD_TYPE_30));
-        assertThrows(
-                IOException.class,
-                () ->
-                        new BluetoothPbapVcardList(
-                                ACCOUNT, is, PbapClientConnectionHandler.VCARD_TYPE_21));
-    }
-
-    @Test
-    public void constructor_withInvalidVcardType_throwsIllegalArgumentException() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () ->
-                        new BluetoothPbapVcardList(
-                                ACCOUNT,
-                                new ByteArrayInputStream("Hello world".getBytes()),
-                                (byte) -1));
-    }
-
-    @Test
-    public void test30ParserWith21Vcard_parsingSucceeds() throws IOException {
-        InputStream fileStream =
-                mTestResources.openRawResource(com.android.bluetooth.tests.R.raw.v21_simple);
-        BluetoothPbapVcardList result =
-                new BluetoothPbapVcardList(
-                        ACCOUNT, fileStream, PbapClientConnectionHandler.VCARD_TYPE_30);
-        assertThat(result.getCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void test21ParserWith30Vcard_parsingSucceeds() throws IOException {
-        InputStream fileStream =
-                mTestResources.openRawResource(com.android.bluetooth.tests.R.raw.v30_simple);
-        BluetoothPbapVcardList result =
-                new BluetoothPbapVcardList(
-                        ACCOUNT, fileStream, PbapClientConnectionHandler.VCARD_TYPE_21);
-        assertThat(result.getCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void test30ParserWithUnsupportedVcardVersion_parsingFails() throws IOException {
-        InputStream fileStream =
-                mTestResources.openRawResource(
-                        com.android.bluetooth.tests.R.raw.unsupported_version);
-        BluetoothPbapVcardList result =
-                new BluetoothPbapVcardList(
-                        ACCOUNT, fileStream, PbapClientConnectionHandler.VCARD_TYPE_30);
-        assertThat(result.getCount()).isEqualTo(0);
-    }
-}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java
index a733160..e6cf835 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java
@@ -75,7 +75,7 @@
 
     @Test
     public void testToString() {
-        final String path = PbapClientConnectionHandler.ICH_PATH;
+        final String path = PbapPhonebook.ICH_PATH;
         final CallLogPullRequest request =
                 new CallLogPullRequest(mTargetContext, path, mCallCounter, mAccount);
 
@@ -84,7 +84,7 @@
 
     @Test
     public void onPullComplete_whenResultsAreNull() {
-        final String path = PbapClientConnectionHandler.ICH_PATH;
+        final String path = PbapPhonebook.ICH_PATH;
         final CallLogPullRequest request =
                 new CallLogPullRequest(mTargetContext, path, mCallCounter, mAccount);
         request.setResults(null);
@@ -111,7 +111,7 @@
 
     @Test
     public void onPullComplete_whenResultsAreEmpty() {
-        final String path = PbapClientConnectionHandler.ICH_PATH;
+        final String path = PbapPhonebook.ICH_PATH;
         final CallLogPullRequest request =
                 new CallLogPullRequest(mTargetContext, path, mCallCounter, mAccount);
         List<VCardEntry> results = new ArrayList<>();
@@ -125,7 +125,7 @@
 
     @Test
     public void onPullComplete_whenThereIsNoPhoneProperty() {
-        final String path = PbapClientConnectionHandler.MCH_PATH;
+        final String path = PbapPhonebook.MCH_PATH;
         final CallLogPullRequest request =
                 new CallLogPullRequest(mTargetContext, path, mCallCounter, mAccount);
 
@@ -149,7 +149,7 @@
 
     @Test
     public void onPullComplete_success() {
-        final String path = PbapClientConnectionHandler.OCH_PATH;
+        final String path = PbapPhonebook.OCH_PATH;
         final CallLogPullRequest request =
                 new CallLogPullRequest(mTargetContext, path, mCallCounter, mAccount);
         List<VCardEntry> results = new ArrayList<>();
@@ -178,7 +178,7 @@
 
     @Test
     public void updateTimesContacted_cursorIsClosed() {
-        final String path = PbapClientConnectionHandler.OCH_PATH;
+        final String path = PbapPhonebook.OCH_PATH;
         final CallLogPullRequest request =
                 new CallLogPullRequest(mTargetContext, path, mCallCounter, mAccount);
         mCallCounter.put("key", 1);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapApplicationParametersTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapApplicationParametersTest.java
new file mode 100644
index 0000000..81c90a9
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapApplicationParametersTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.pbapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PbapApplicationParametersTest {
+
+    public static final int TEST_LIST_SIZE = 250;
+    public static final int TEST_LIST_SIZE_TOO_SMALL = -1;
+    public static final int TEST_LIST_SIZE_TOO_LARGE = 65536;
+
+    public static final int TEST_LIST_OFFSET = 0;
+    public static final int TEST_LIST_OFFSET_TOO_SMALL = -1;
+    public static final int TEST_LIST_OFFSET_TOO_LARGE = 65536;
+
+    public static final String ALL_PROPERTIES_TO_STRING =
+            "<PbapApplicationParameters properties=PROPERTIES_ALL format=1 maxListCount=250"
+                    + " listStartOffset=0>";
+    public static final String FILTER_PROPERTIES_TO_STRING =
+            "<PbapApplicationParameters properties=[PROPERTY_VERSION PROPERTY_FN PROPERTY_N]"
+                    + " format=1 maxListCount=250 listStartOffset=0>";
+    public static final String FILTER_INDIVIDUAL_PROPERTIES_TO_STRING =
+            "<PbapApplicationParameters properties=[PROPERTY_VERSION PROPERTY_FN PROPERTY_N"
+                    + " PROPERTY_PHOTO PROPERTY_ADR PROPERTY_TEL PROPERTY_EMAIL PROPERTY_NICKNAME]"
+                    + " format=1 maxListCount=250 listStartOffset=0>";
+    public static final String FORMAT_21_TO_STRING =
+            "<PbapApplicationParameters properties=PROPERTIES_ALL format=0 maxListCount=250"
+                    + " listStartOffset=0>";
+
+    @Test
+    public void testCreateParams_paramsWellFormed() {
+        PbapApplicationParameters params =
+                new PbapApplicationParameters(
+                        PbapApplicationParameters.PROPERTIES_ALL,
+                        PbapPhonebook.FORMAT_VCARD_30,
+                        TEST_LIST_SIZE,
+                        TEST_LIST_OFFSET);
+
+        assertThat(params.getPropertySelectorMask())
+                .isEqualTo(PbapApplicationParameters.PROPERTIES_ALL);
+        assertThat(params.getVcardFormat()).isEqualTo(PbapPhonebook.FORMAT_VCARD_30);
+        assertThat(params.getMaxListCount()).isEqualTo(TEST_LIST_SIZE);
+        assertThat(params.getListStartOffset()).isEqualTo(TEST_LIST_OFFSET);
+        assertThat(params.toString()).isEqualTo(ALL_PROPERTIES_TO_STRING);
+    }
+
+    @Test
+    public void testCreateParams_withPropertyFilter_paramsWellFormed() {
+        PbapApplicationParameters params =
+                new PbapApplicationParameters(
+                        (PbapApplicationParameters.PROPERTY_VERSION
+                                | PbapApplicationParameters.PROPERTY_FN
+                                | PbapApplicationParameters.PROPERTY_N),
+                        PbapPhonebook.FORMAT_VCARD_30,
+                        TEST_LIST_SIZE,
+                        TEST_LIST_OFFSET);
+
+        assertThat(params.getPropertySelectorMask())
+                .isEqualTo(
+                        (PbapApplicationParameters.PROPERTY_VERSION
+                                | PbapApplicationParameters.PROPERTY_FN
+                                | PbapApplicationParameters.PROPERTY_N));
+        assertThat(params.getVcardFormat()).isEqualTo(PbapPhonebook.FORMAT_VCARD_30);
+        assertThat(params.getMaxListCount()).isEqualTo(TEST_LIST_SIZE);
+        assertThat(params.getListStartOffset()).isEqualTo(TEST_LIST_OFFSET);
+        assertThat(params.toString()).isEqualTo(FILTER_PROPERTIES_TO_STRING);
+    }
+
+    @Test
+    public void testCreateParams_withAllPropertiesIndividually_paramsWellFormed() {
+        PbapApplicationParameters params =
+                new PbapApplicationParameters(
+                        (PbapApplicationParameters.PROPERTY_VERSION
+                                | PbapApplicationParameters.PROPERTY_FN
+                                | PbapApplicationParameters.PROPERTY_N
+                                | PbapApplicationParameters.PROPERTY_PHOTO
+                                | PbapApplicationParameters.PROPERTY_ADR
+                                | PbapApplicationParameters.PROPERTY_TEL
+                                | PbapApplicationParameters.PROPERTY_EMAIL
+                                | PbapApplicationParameters.PROPERTY_NICKNAME),
+                        PbapPhonebook.FORMAT_VCARD_30,
+                        TEST_LIST_SIZE,
+                        TEST_LIST_OFFSET);
+
+        assertThat(params.getPropertySelectorMask())
+                .isEqualTo(
+                        (PbapApplicationParameters.PROPERTY_VERSION
+                                | PbapApplicationParameters.PROPERTY_FN
+                                | PbapApplicationParameters.PROPERTY_N
+                                | PbapApplicationParameters.PROPERTY_PHOTO
+                                | PbapApplicationParameters.PROPERTY_ADR
+                                | PbapApplicationParameters.PROPERTY_TEL
+                                | PbapApplicationParameters.PROPERTY_EMAIL
+                                | PbapApplicationParameters.PROPERTY_NICKNAME));
+        assertThat(params.getVcardFormat()).isEqualTo(PbapPhonebook.FORMAT_VCARD_30);
+        assertThat(params.getMaxListCount()).isEqualTo(TEST_LIST_SIZE);
+        assertThat(params.getListStartOffset()).isEqualTo(TEST_LIST_OFFSET);
+        assertThat(params.toString()).isEqualTo(FILTER_INDIVIDUAL_PROPERTIES_TO_STRING);
+    }
+
+    @Test
+    public void testCreateParams_withFormat21_paramsWellFormed() {
+        PbapApplicationParameters params =
+                new PbapApplicationParameters(
+                        PbapApplicationParameters.PROPERTIES_ALL,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        TEST_LIST_SIZE,
+                        TEST_LIST_OFFSET);
+
+        assertThat(params.getPropertySelectorMask())
+                .isEqualTo(PbapApplicationParameters.PROPERTIES_ALL);
+        assertThat(params.getVcardFormat()).isEqualTo(PbapPhonebook.FORMAT_VCARD_21);
+        assertThat(params.getMaxListCount()).isEqualTo(TEST_LIST_SIZE);
+        assertThat(params.getListStartOffset()).isEqualTo(TEST_LIST_OFFSET);
+        assertThat(params.toString()).isEqualTo(FORMAT_21_TO_STRING);
+    }
+
+    @Test
+    public void testCreateParams_maxListCountTooLarge_exceptionThrown() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new PbapApplicationParameters(
+                                PbapApplicationParameters.PROPERTIES_ALL,
+                                PbapPhonebook.FORMAT_VCARD_30,
+                                TEST_LIST_SIZE_TOO_LARGE,
+                                TEST_LIST_OFFSET));
+    }
+
+    @Test
+    public void testCreateParams_maxListCountTooSmall_exceptionThrown() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new PbapApplicationParameters(
+                                PbapApplicationParameters.PROPERTIES_ALL,
+                                PbapPhonebook.FORMAT_VCARD_30,
+                                TEST_LIST_SIZE_TOO_SMALL,
+                                TEST_LIST_OFFSET));
+    }
+
+    @Test
+    public void testCreateParams_OffsetTooLarge_exceptionThrown() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new PbapApplicationParameters(
+                                PbapApplicationParameters.PROPERTIES_ALL,
+                                PbapPhonebook.FORMAT_VCARD_30,
+                                TEST_LIST_SIZE,
+                                TEST_LIST_OFFSET_TOO_LARGE));
+    }
+
+    @Test
+    public void testCreateParams_OffsetTooSmall_exceptionThrown() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new PbapApplicationParameters(
+                                PbapApplicationParameters.PROPERTIES_ALL,
+                                PbapPhonebook.FORMAT_VCARD_30,
+                                TEST_LIST_SIZE,
+                                TEST_LIST_OFFSET_TOO_LARGE));
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/AuthenticationServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorServiceTest.java
similarity index 88%
rename from android/app/tests/unit/src/com/android/bluetooth/pbapclient/AuthenticationServiceTest.java
rename to android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorServiceTest.java
index 5464531..e25c542 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/AuthenticationServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorServiceTest.java
@@ -38,7 +38,7 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
-public class AuthenticationServiceTest {
+public class PbapClientAccountAuthenticatorServiceTest {
 
     Context mTargetContext;
 
@@ -58,7 +58,7 @@
     @Test
     public void bind() throws Exception {
         Intent intent = new Intent("android.accounts.AccountAuthenticator");
-        intent.setClass(mTargetContext, AuthenticationService.class);
+        intent.setClass(mTargetContext, PbapClientAccountAuthenticatorService.class);
 
         assertThat(mServiceRule.bindService(intent)).isNotNull();
     }
@@ -66,7 +66,8 @@
     private void enableService(boolean enable) {
         int enabledState =
                 enable ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DEFAULT;
-        ComponentName serviceName = new ComponentName(mTargetContext, AuthenticationService.class);
+        ComponentName serviceName =
+                new ComponentName(mTargetContext, PbapClientAccountAuthenticatorService.class);
         mTargetContext
                 .getPackageManager()
                 .setComponentEnabledSetting(serviceName, enabledState, DONT_KILL_APP);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/AuthenticatorTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorTest.java
similarity index 93%
rename from android/app/tests/unit/src/com/android/bluetooth/pbapclient/AuthenticatorTest.java
rename to android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorTest.java
index 8ebce0a..6b63604 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/AuthenticatorTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountAuthenticatorTest.java
@@ -37,10 +37,10 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class AuthenticatorTest {
+public class PbapClientAccountAuthenticatorTest {
 
     private Context mTargetContext;
-    private Authenticator mAuthenticator;
+    private PbapClientAccountAuthenticator mAuthenticator;
 
     @Mock AccountAuthenticatorResponse mResponse;
 
@@ -49,7 +49,7 @@
     @Before
     public void setUp() throws Exception {
         mTargetContext = InstrumentationRegistry.getTargetContext();
-        mAuthenticator = new Authenticator(mTargetContext);
+        mAuthenticator = new PbapClientAccountAuthenticator(mTargetContext);
     }
 
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountManagerTest.java
new file mode 100644
index 0000000..1611399
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountManagerTest.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.nullable;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.HandlerThread;
+import android.os.UserManager;
+import android.os.test.TestLooper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.TestUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class PbapClientAccountManagerTest {
+    private static final String ACCOUNT_TYPE = "com.android.bluetooth.pbapclient.account";
+
+    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+    private BluetoothAdapter mAdapter;
+
+    @Mock private Context mMockContext;
+    @Mock private HandlerThread mMockHandlerThread;
+    private TestLooper mTestLooper;
+    @Mock private Resources mMockResources;
+    @Mock private AccountManager mMockAccountManager;
+    @Mock private UserManager mMockUserManager;
+    ArgumentCaptor<BroadcastReceiver> mReceiverCaptor =
+            ArgumentCaptor.forClass(BroadcastReceiver.class);
+    private BroadcastReceiver mBroadcastReceiver;
+    @Mock private PbapClientAccountManager.Callback mMockCallback;
+    ArgumentCaptor<List<Account>> mFromAccountsCaptor = ArgumentCaptor.forClass(List.class);
+    ArgumentCaptor<List<Account>> mToAccountsCaptor = ArgumentCaptor.forClass(List.class);
+
+    private PbapClientAccountManager mAccountManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        assertThat(mAdapter).isNotNull();
+
+        TestUtils.mockGetSystemService(
+                mMockContext, Context.ACCOUNT_SERVICE, AccountManager.class, mMockAccountManager);
+        setAvailableAccounts(new Account[] {});
+        setAccountVisibility(AccountManager.VISIBILITY_NOT_VISIBLE);
+        doReturn(true)
+                .when(mMockAccountManager)
+                .addAccountExplicitly(
+                        any(Account.class), nullable(String.class), nullable(Bundle.class));
+        doReturn(true).when(mMockAccountManager).removeAccountExplicitly(any(Account.class));
+
+        TestUtils.mockGetSystemService(
+                mMockContext, Context.USER_SERVICE, UserManager.class, mMockUserManager);
+        doReturn("").when(mMockContext).getPackageName();
+        doReturn(false).when(mMockUserManager).isUserUnlocked();
+
+        doReturn(mMockResources).when(mMockContext).getResources();
+        doReturn(ACCOUNT_TYPE).when(mMockResources).getString(anyInt());
+
+        mTestLooper = new TestLooper();
+        doReturn(mTestLooper.getLooper()).when(mMockHandlerThread).getLooper();
+
+        mAccountManager =
+                new PbapClientAccountManager(mMockContext, mMockHandlerThread, mMockCallback);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mAccountManager != null) {
+            mAccountManager.stop();
+            mAccountManager = null;
+        }
+    }
+
+    // *********************************************************************************************
+    // * Public API Methods
+    // *********************************************************************************************
+
+    // getAccountForDevice
+
+    @Test
+    public void testGetAccountForDevice_deviceAccountNameAndTypeAreValid() {
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(account.name).isEqualTo(device.getAddress());
+        assertThat(account.type).isEqualTo(ACCOUNT_TYPE);
+    }
+
+    @Test
+    public void testGetAccountForDevice_withNullDevice_throwsIllegalArgumentException() {
+        assertThrows(
+                IllegalArgumentException.class, () -> mAccountManager.getAccountForDevice(null));
+    }
+
+    // Start/Initialization Proceedures
+
+    @Test
+    public void testStartAccountManager_userUnlockedAccountVisibleNoAccounts_accountsInitialized() {
+        doReturn(true).when(mMockUserManager).isUserUnlocked();
+        setAccountVisibility(AccountManager.VISIBILITY_VISIBLE);
+        startAccountManager();
+        mTestLooper.dispatchAll();
+
+        verify(mMockCallback, times(1))
+                .onAccountsChanged(mFromAccountsCaptor.capture(), mToAccountsCaptor.capture());
+        assertThat(mFromAccountsCaptor.getValue()).isNull();
+        assertThat(mToAccountsCaptor.getValue()).isNotNull();
+        assertThat(mToAccountsCaptor.getValue()).isEmpty();
+    }
+
+    @Test
+    public void testStartAccountManager_userUnlockedAccountVisibleHasAccount_accountsInitialized() {
+        BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        BluetoothDevice device2 = TestUtils.getTestDevice(mAdapter, /* id= */ 2);
+        Account[] accounts =
+                new Account[] {getAccountForDevice(device1), getAccountForDevice(device2)};
+
+        doReturn(true).when(mMockUserManager).isUserUnlocked();
+        setAccountVisibility(AccountManager.VISIBILITY_VISIBLE);
+        setAvailableAccounts(accounts);
+        startAccountManager();
+        mTestLooper.dispatchAll();
+
+        verify(mMockCallback, times(1))
+                .onAccountsChanged(mFromAccountsCaptor.capture(), mToAccountsCaptor.capture());
+        List<Account> fromAccounts = mFromAccountsCaptor.getValue();
+        List<Account> toAccounts = mToAccountsCaptor.getValue();
+
+        assertThat(fromAccounts).isNull();
+        assertThat(toAccounts).isNotNull();
+        assertThat(toAccounts.size()).isEqualTo(2);
+        assertThat(toAccounts).contains(accounts[0]);
+        assertThat(toAccounts).contains(accounts[1]);
+    }
+
+    @Test
+    public void testStartAccountManager_userUnlockedAndAccountNotVisible_accountChecksBegin() {
+        doReturn(true).when(mMockUserManager).isUserUnlocked();
+        startAccountManager();
+        mTestLooper.dispatchAll();
+
+        verify(mMockAccountManager, times(1)).getAccountVisibility(any(Account.class), anyString());
+    }
+
+    @Test
+    public void testStartAccountManager_userNotUnlocked_noAccountsOrChecks() {
+        doReturn(false).when(mMockUserManager).isUserUnlocked();
+        startAccountManager();
+        mTestLooper.dispatchAll();
+
+        verify(mMockAccountManager, never()).getAccountVisibility(any(Account.class), anyString());
+        verify(mMockCallback, never()).onAccountsChanged(any(List.class), any(List.class));
+    }
+
+    @Test
+    public void testReceiveUserLocked_accountNotVisible_accountChecksBegin() {
+        testStartAccountManager_userNotUnlocked_noAccountsOrChecks();
+        sendUserUnlocked();
+        mTestLooper.dispatchAll();
+
+        verify(mMockAccountManager, times(1)).getAccountVisibility(any(Account.class), anyString());
+    }
+
+    @Test
+    public void testReceiveUserLocked_accountVisible_accountsInitialized() {
+        testStartAccountManager_userNotUnlocked_noAccountsOrChecks();
+        setAccountVisibility(AccountManager.VISIBILITY_VISIBLE);
+        sendUserUnlocked();
+        mTestLooper.dispatchAll();
+
+        verify(mMockCallback, times(1))
+                .onAccountsChanged(mFromAccountsCaptor.capture(), mToAccountsCaptor.capture());
+        List<Account> fromAccounts = mFromAccountsCaptor.getValue();
+        List<Account> toAccounts = mToAccountsCaptor.getValue();
+
+        assertThat(fromAccounts).isNull();
+        assertThat(toAccounts).isNotNull();
+        assertThat(toAccounts).isEmpty();
+    }
+
+    @Test
+    public void testAccountVisibilityCheck_notReady_retryQueued() {
+        testStartAccountManager_userUnlockedAndAccountNotVisible_accountChecksBegin();
+
+        mTestLooper.moveTimeForward(2000);
+        mTestLooper.dispatchAll();
+        verify(mMockAccountManager, times(2)).getAccountVisibility(any(Account.class), anyString());
+    }
+
+    // getAccounts
+
+    @Test
+    public void testGetAccounts_storageInitializedWithAccounts_returnsAccountList() {
+        BluetoothDevice device1 = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        BluetoothDevice device2 = TestUtils.getTestDevice(mAdapter, /* id= */ 2);
+        Account[] accounts =
+                new Account[] {getAccountForDevice(device1), getAccountForDevice(device2)};
+
+        doReturn(true).when(mMockUserManager).isUserUnlocked();
+        setAccountVisibility(AccountManager.VISIBILITY_VISIBLE);
+        setAvailableAccounts(accounts);
+        startAccountManager();
+        mTestLooper.dispatchAll();
+
+        assertThat(mAccountManager.getAccounts()).isNotNull();
+        assertThat(mAccountManager.getAccounts().size()).isEqualTo(2);
+        assertThat(mAccountManager.getAccounts()).contains(accounts[0]);
+        assertThat(mAccountManager.getAccounts()).contains(accounts[1]);
+    }
+
+    @Test
+    public void testGetAccounts_storageNotInitialized_returnsEmptyList() {
+        assertThat(mAccountManager.getAccounts()).isEmpty();
+    }
+
+    // addAccount/addAccountInternal
+
+    @Test
+    public void testAddAccount_accountDoesNotExist_accountInAccountsListAndReturnsTrue() {
+        testStartAccountManager_userUnlockedAccountVisibleNoAccounts_accountsInitialized();
+
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(mAccountManager.addAccount(account)).isTrue();
+
+        verify(mMockCallback, times(2))
+                .onAccountsChanged(mFromAccountsCaptor.capture(), mToAccountsCaptor.capture());
+        List<Account> toAccounts = mToAccountsCaptor.getValue();
+        assertThat(toAccounts).contains(account);
+
+        assertThat(mAccountManager.getAccounts()).contains(account);
+    }
+
+    @Test
+    public void testAddAccount_accountAlreadyExists_accountInAccountsListAndReturnsTrue() {
+        testStartAccountManager_userUnlockedAccountVisibleNoAccounts_accountsInitialized();
+
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(mAccountManager.addAccount(account)).isTrue();
+        assertThat(mAccountManager.getAccounts()).contains(account);
+
+        // Add again once its already in there
+        assertThat(mAccountManager.addAccount(account)).isTrue();
+        assertThat(mAccountManager.getAccounts().size()).isEqualTo(1);
+        assertThat(mAccountManager.getAccounts()).contains(account);
+    }
+
+    @Test
+    public void testAddAccounts_accountManagerOperationFails_returnsFalse() {
+        doReturn(false)
+                .when(mMockAccountManager)
+                .addAccountExplicitly(
+                        any(Account.class), nullable(String.class), nullable(Bundle.class));
+
+        testStartAccountManager_userUnlockedAccountVisibleNoAccounts_accountsInitialized();
+
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(mAccountManager.addAccount(account)).isFalse();
+        assertThat(mAccountManager.getAccounts()).isEmpty();
+    }
+
+    @Test
+    public void testAddAccounts_storageNotInitialized_returnsFalse() {
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(mAccountManager.addAccount(account)).isFalse();
+    }
+
+    // removeAccount/removeAccountInternal
+
+    @Test
+    public void testRemoveAccount_accountExists_accountNotInAccountsListAndReturnsTrue() {
+        testStartAccountManager_userUnlockedAccountVisibleNoAccounts_accountsInitialized();
+
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(mAccountManager.addAccount(account)).isTrue();
+        assertThat(mAccountManager.getAccounts()).contains(account);
+
+        // Remove Account
+        assertThat(mAccountManager.removeAccount(account)).isTrue();
+        assertThat(mAccountManager.getAccounts()).isEmpty();
+    }
+
+    @Test
+    public void testRemoveAccount_accountDoesNotExist_accountNotInAccountsListAndReturnsTrue() {
+        testStartAccountManager_userUnlockedAccountVisibleNoAccounts_accountsInitialized();
+
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(mAccountManager.addAccount(account)).isTrue();
+        assertThat(mAccountManager.getAccounts()).contains(account);
+
+        BluetoothDevice device2 = TestUtils.getTestDevice(mAdapter, /* id= */ 2);
+        Account account2 = mAccountManager.getAccountForDevice(device2);
+        assertThat(mAccountManager.getAccounts()).doesNotContain(account2);
+        assertThat(mAccountManager.removeAccount(account2)).isTrue();
+        assertThat(mAccountManager.getAccounts()).contains(account);
+    }
+
+    @Test
+    public void testRemoveAccounts_accountManagerOperationFails_returnsFalse() {
+        testStartAccountManager_userUnlockedAccountVisibleNoAccounts_accountsInitialized();
+
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(mAccountManager.addAccount(account)).isTrue();
+        assertThat(mAccountManager.getAccounts()).contains(account);
+
+        doReturn(false).when(mMockAccountManager).removeAccountExplicitly(any(Account.class));
+        assertThat(mAccountManager.removeAccount(account)).isFalse();
+        assertThat(mAccountManager.getAccounts()).contains(account);
+    }
+
+    @Test
+    public void testRemoveAccounts_storageNotInitialized_returnsFalse() {
+        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, /* id= */ 1);
+        Account account = mAccountManager.getAccountForDevice(device);
+        assertThat(mAccountManager.removeAccount(account)).isFalse();
+    }
+
+    // *********************************************************************************************
+    // * Debug/Dump/toString()
+    // *********************************************************************************************
+
+    @Test
+    public void testDump() {
+        String dumpContents = mAccountManager.dump();
+        assertThat(dumpContents).isNotNull();
+        assertThat(dumpContents.length()).isNotEqualTo(0);
+    }
+
+    // *********************************************************************************************
+    // * Test Utilities
+    // *********************************************************************************************
+
+    private void startAccountManager() {
+        mAccountManager.start();
+        verify(mMockContext).registerReceiver(mReceiverCaptor.capture(), any(IntentFilter.class));
+        mBroadcastReceiver = mReceiverCaptor.getValue();
+    }
+
+    private void sendUserUnlocked() {
+        doReturn(true).when(mMockUserManager).isUserUnlocked();
+        Intent intent = new Intent(Intent.ACTION_USER_UNLOCKED);
+        mBroadcastReceiver.onReceive(mMockContext, intent);
+    }
+
+    private Account getAccountForDevice(BluetoothDevice device) {
+        return new Account(device.getAddress(), "com.android.bluetooth.pbabclient.account");
+    }
+
+    private void setAccountVisibility(int visibility) {
+        doReturn(visibility)
+                .when(mMockAccountManager)
+                .getAccountVisibility(any(Account.class), anyString());
+    }
+
+    private void setAvailableAccounts(Account[] accounts) {
+        doReturn(accounts).when(mMockAccountManager).getAccountsByType(anyString());
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientBinderTest.java
new file mode 100644
index 0000000..5bde803
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientBinderTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.AttributionSource;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.TestUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class PbapClientBinderTest {
+    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+    @Mock private PbapClientService mMockService;
+    private BluetoothDevice mTestDevice;
+    private AttributionSource mAttributionSource;
+
+    private PbapClientBinder mPbapClientBinder;
+
+    @Before
+    public void setUp() throws Exception {
+        mTestDevice = TestUtils.getTestDevice(BluetoothAdapter.getDefaultAdapter(), 1);
+        mAttributionSource = new AttributionSource.Builder(1).build();
+        mPbapClientBinder = new PbapClientBinder(mMockService);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mPbapClientBinder != null) {
+            mPbapClientBinder.cleanup();
+            mPbapClientBinder = null;
+        }
+    }
+
+    // *********************************************************************************************
+    // * API Methods
+    // *********************************************************************************************
+
+    @Test
+    public void testConnect() {
+        mPbapClientBinder.connect(mTestDevice, mAttributionSource);
+        verify(mMockService).connect(eq(mTestDevice));
+    }
+
+    @Test
+    public void testDisconnect() {
+        mPbapClientBinder.disconnect(mTestDevice, mAttributionSource);
+        verify(mMockService).disconnect(eq(mTestDevice));
+    }
+
+    @Test
+    public void testGetConnectedDevices() {
+        mPbapClientBinder.getConnectedDevices(mAttributionSource);
+        verify(mMockService).getConnectedDevices();
+    }
+
+    @Test
+    public void testGetDevicesMatchingConnectionStates() {
+        int[] states = new int[] {BluetoothProfile.STATE_CONNECTED};
+        mPbapClientBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
+        verify(mMockService).getDevicesMatchingConnectionStates(eq(states));
+    }
+
+    @Test
+    public void testGetConnectionState() {
+        mPbapClientBinder.getConnectionState(mTestDevice, mAttributionSource);
+        verify(mMockService).getConnectionState(eq(mTestDevice));
+    }
+
+    @Test
+    public void testSetConnectionPolicy() {
+        int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+        mPbapClientBinder.setConnectionPolicy(mTestDevice, connectionPolicy, mAttributionSource);
+        verify(mMockService).setConnectionPolicy(eq(mTestDevice), eq(connectionPolicy));
+    }
+
+    @Test
+    public void testGetConnectionPolicy() {
+        mPbapClientBinder.getConnectionPolicy(mTestDevice, mAttributionSource);
+        verify(mMockService).getConnectionPolicy(eq(mTestDevice));
+    }
+
+    // *********************************************************************************************
+    // * API Methods (Without service set, i.e. profile not up)
+    // *********************************************************************************************
+
+    @Test
+    public void testConnect_afterCleanup_returnsFalse() {
+        mPbapClientBinder.cleanup();
+        boolean result = mPbapClientBinder.connect(mTestDevice, mAttributionSource);
+        verify(mMockService, times(0)).connect(any(BluetoothDevice.class));
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void testDisconnect_afterCleanup_returnsFalse() {
+        mPbapClientBinder.cleanup();
+        boolean result = mPbapClientBinder.disconnect(mTestDevice, mAttributionSource);
+        verify(mMockService, times(0)).disconnect(any(BluetoothDevice.class));
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void testGetConnectedDevices_afterCleanup_returnsEmptyList() {
+        mPbapClientBinder.cleanup();
+        List<BluetoothDevice> devices = mPbapClientBinder.getConnectedDevices(mAttributionSource);
+        verify(mMockService, times(0)).getConnectedDevices();
+        assertThat(devices).isEmpty();
+    }
+
+    @Test
+    public void testGetDevicesMatchingConnectionStates_afterCleanup_returnsEmptyList() {
+        mPbapClientBinder.cleanup();
+        int[] states = new int[] {BluetoothProfile.STATE_CONNECTED};
+        List<BluetoothDevice> devices =
+                mPbapClientBinder.getDevicesMatchingConnectionStates(states, mAttributionSource);
+        verify(mMockService, times(0)).getDevicesMatchingConnectionStates(any(int[].class));
+        assertThat(devices).isEmpty();
+    }
+
+    @Test
+    public void testGetConnectionState_afterCleanup_returnsDisconnected() {
+        mPbapClientBinder.cleanup();
+        int state = mPbapClientBinder.getConnectionState(mTestDevice, mAttributionSource);
+        verify(mMockService, times(0)).getConnectionState(any(BluetoothDevice.class));
+        assertThat(state).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+    }
+
+    @Test
+    public void testSetConnectionPolicy_afterCleanup_returnsFalse() {
+        mPbapClientBinder.cleanup();
+        int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+        boolean result =
+                mPbapClientBinder.setConnectionPolicy(
+                        mTestDevice, connectionPolicy, mAttributionSource);
+        verify(mMockService, times(0)).setConnectionPolicy(any(BluetoothDevice.class), anyInt());
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void testGetConnectionPolicy_afterCleanup_returnsUnknown() {
+        mPbapClientBinder.cleanup();
+        int result = mPbapClientBinder.getConnectionPolicy(mTestDevice, mAttributionSource);
+        verify(mMockService, times(0)).getConnectionPolicy(any(BluetoothDevice.class));
+        assertThat(result).isEqualTo(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandlerTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandlerTest.java
index aea3758..5d5220b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandlerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandlerTest.java
@@ -18,28 +18,23 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.SdpPseRecord;
 import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
+import android.content.res.Resources;
 import android.os.HandlerThread;
 import android.os.Looper;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.bluetooth.TestUtils;
-import com.android.bluetooth.btservice.AdapterService;
-import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -57,20 +52,23 @@
     private static final String TAG = "ConnHandlerTest";
     private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
 
+    // Normal supported features for our client
+    private static final int SUPPORTED_FEATURES =
+            PbapSdpRecord.FEATURE_DOWNLOADING | PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT;
+
     private HandlerThread mThread;
     private Looper mLooper;
-    private Context mTargetContext;
     private BluetoothDevice mRemoteDevice;
 
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
-    @Mock private AdapterService mAdapterService;
-
-    @Mock private DatabaseManager mDatabaseManager;
-
     private BluetoothAdapter mAdapter;
 
-    private PbapClientService mService;
+    @Mock private PbapClientService mService;
+
+    @Mock private Resources mMockResources;
+
+    @Mock private ContentResolver mMockContentResolver;
 
     @Mock private PbapClientStateMachine mStateMachine;
 
@@ -78,20 +76,10 @@
 
     @Before
     public void setUp() throws Exception {
-        mTargetContext =
-                spy(
-                        new ContextWrapper(
-                                InstrumentationRegistry.getInstrumentation().getTargetContext()));
-
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
 
-        TestUtils.setAdapterService(mAdapterService);
-        doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
-        mService = new PbapClientService(mTargetContext);
-        mService.start();
-
         mAdapter = BluetoothAdapter.getDefaultAdapter();
 
         mThread = new HandlerThread("test_handler_thread");
@@ -99,23 +87,23 @@
         mLooper = mThread.getLooper();
         mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
 
-        when(mStateMachine.getContext()).thenReturn(mTargetContext);
+        doReturn(mService).when(mStateMachine).getContext();
+        doReturn(mMockContentResolver).when(mService).getContentResolver();
+        doReturn(mMockResources).when(mService).getResources();
+        doReturn("com.android.bluetooth.pbapclient").when(mMockResources).getString(anyInt());
 
         mHandler =
                 new PbapClientConnectionHandler.Builder()
                         .setLooper(mLooper)
+                        .setLocalSupportedFeatures(SUPPORTED_FEATURES)
                         .setClientSM(mStateMachine)
-                        .setContext(mTargetContext)
+                        .setService(mService)
                         .setRemoteDevice(mRemoteDevice)
                         .build();
     }
 
     @After
     public void tearDown() throws Exception {
-        mService.stop();
-        mService = PbapClientService.getPbapClientService();
-        assertThat(mService).isNull();
-        TestUtils.clearAdapterService(mAdapterService);
         mLooper.quit();
     }
 
@@ -126,7 +114,7 @@
 
     @Test
     public void connectSocket_whenBluetoothIsNotEnabled_returnsFalse_withInvalidL2capPsm() {
-        SdpPseRecord record = mock(SdpPseRecord.class);
+        PbapSdpRecord record = mock(PbapSdpRecord.class);
         mHandler.setPseRecord(record);
 
         when(record.getL2capPsm()).thenReturn(PbapClientConnectionHandler.L2CAP_INVALID_PSM);
@@ -135,7 +123,7 @@
 
     @Test
     public void connectSocket_whenBluetoothIsNotEnabled_returnsFalse_withValidL2capPsm() {
-        SdpPseRecord record = mock(SdpPseRecord.class);
+        PbapSdpRecord record = mock(PbapSdpRecord.class);
         mHandler.setPseRecord(record);
 
         when(record.getL2capPsm()).thenReturn(1); // Valid PSM ranges 1 to 30;
@@ -151,7 +139,7 @@
 
     @Test
     public void abort() {
-        SdpPseRecord record = mock(SdpPseRecord.class);
+        PbapSdpRecord record = mock(PbapSdpRecord.class);
         when(record.getL2capPsm()).thenReturn(1); // Valid PSM ranges 1 to 30;
         mHandler.setPseRecord(record);
         mHandler.connectSocket(); // Workaround for setting mSocket as non-null value
@@ -165,34 +153,14 @@
 
     @Test
     public void removeCallLog_doesNotCrash() {
-        ContentResolver res = mock(ContentResolver.class);
-        when(mTargetContext.getContentResolver()).thenReturn(res);
         mHandler.removeCallLog();
 
         // Also test when content resolver is null.
-        when(mTargetContext.getContentResolver()).thenReturn(null);
+        when(mService.getContentResolver()).thenReturn(null);
         mHandler.removeCallLog();
     }
 
     @Test
-    public void isRepositorySupported_withoutSettingPseRecord_returnsFalse() {
-        mHandler.setPseRecord(null);
-        final int mask = 0x11;
-
-        assertThat(mHandler.isRepositorySupported(mask)).isFalse();
-    }
-
-    @Test
-    public void isRepositorySupported_withSettingPseRecord() {
-        SdpPseRecord record = mock(SdpPseRecord.class);
-        when(record.getSupportedRepositories()).thenReturn(1);
-        mHandler.setPseRecord(record);
-        final int mask = 0x11;
-
-        assertThat(mHandler.isRepositorySupported(mask)).isTrue();
-    }
-
-    @Test
     public void createAndDisconnectWithoutAddingAccount_doesNotCrash() {
         mHandler.obtainMessage(PbapClientConnectionHandler.MSG_DISCONNECT).sendToTarget();
         TestUtils.waitForLooperToFinishScheduledTask(mHandler.getLooper());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticatorTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexAuthenticatorTest.java
similarity index 91%
rename from android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticatorTest.java
rename to android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexAuthenticatorTest.java
index 64711da..74265a3 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/BluetoothPbapObexAuthenticatorTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexAuthenticatorTest.java
@@ -29,13 +29,13 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class BluetoothPbapObexAuthenticatorTest {
+public class PbapClientObexAuthenticatorTest {
 
-    private BluetoothPbapObexAuthenticator mAuthenticator;
+    private PbapClientObexAuthenticator mAuthenticator;
 
     @Before
     public void setUp() throws Exception {
-        mAuthenticator = new BluetoothPbapObexAuthenticator();
+        mAuthenticator = new PbapClientObexAuthenticator();
     }
 
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java
index fec23eb..e6a99ac 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java
@@ -19,6 +19,8 @@
 
 import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -27,19 +29,27 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
+import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Looper;
+import android.os.UserManager;
 import android.provider.CallLog;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.BluetoothMethodProxy;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
@@ -55,6 +65,9 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class PbapClientServiceTest {
@@ -62,47 +75,80 @@
 
     private PbapClientService mService = null;
     private BluetoothAdapter mAdapter = null;
-    private Context mTargetContext;
     private BluetoothDevice mRemoteDevice;
-    boolean mIsAdapterServiceSet;
-    boolean mIsPbapClientServiceStarted;
 
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
-
-    @Mock private AdapterService mAdapterService;
-
+    @Mock private Context mMockContext;
+    @Mock private AdapterService mMockAdapterService;
     @Mock private DatabaseManager mDatabaseManager;
+    @Mock private PackageManager mMockPackageManager;
+    private MockContentResolver mMockContentResolver;
+    private MockCallLogProvider mMockCallLogProvider;
+    @Mock private Resources mMockResources;
+    @Mock private UserManager mMockUserManager;
+    @Mock private AccountManager mMockAccountManager;
 
     @Before
     public void setUp() throws Exception {
-        mTargetContext = InstrumentationRegistry.getTargetContext();
-        TestUtils.setAdapterService(mAdapterService);
-        mIsAdapterServiceSet = true;
-        doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
-        mService = new PbapClientService(mTargetContext);
-        mService.start();
-        mService.setAvailable(true);
-        mIsPbapClientServiceStarted = true;
-        // Try getting the Bluetooth adapter
+        TestUtils.setAdapterService(mMockAdapterService);
+        doReturn(mDatabaseManager).when(mMockAdapterService).getDatabase();
+
+        doReturn("").when(mMockContext).getPackageName();
+        doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+
+        doReturn(mMockResources).when(mMockContext).getResources();
+        doReturn(Utils.ACCOUNT_TYPE).when(mMockResources).getString(anyInt());
+
+        mMockContentResolver = new MockContentResolver();
+        mMockCallLogProvider = new MockCallLogProvider();
+        mMockContentResolver.addProvider(CallLog.AUTHORITY, mMockCallLogProvider);
+        doReturn(mMockContentResolver).when(mMockContext).getContentResolver();
+
+        doReturn(AccountManager.VISIBILITY_VISIBLE)
+                .when(mMockAccountManager)
+                        .getAccountVisibility(any(Account.class), anyString());
+        doReturn(new Account[]{})
+                .when(mMockAccountManager)
+                        .getAccountsByType(eq(Utils.ACCOUNT_TYPE));
+        TestUtils.mockGetSystemService(
+                mMockContext,
+                Context.ACCOUNT_SERVICE,
+                AccountManager.class,
+                mMockAccountManager);
+
+        doReturn(false).when(mMockUserManager).isUserUnlocked();
+        TestUtils.mockGetSystemService(
+                mMockContext,
+                Context.USER_SERVICE,
+                UserManager.class,
+                mMockUserManager);
+
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         Assert.assertNotNull(mAdapter);
         mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mService = new PbapClientService(mMockContext);
+        mService.start();
+        mService.setAvailable(true);
     }
 
     @After
     public void tearDown() throws Exception {
-        if (!mIsAdapterServiceSet) {
-            return;
-        }
-        if (mIsPbapClientServiceStarted) {
+        if (mService != null) {
             mService.stop();
-            mService = PbapClientService.getPbapClientService();
-            Assert.assertNull(mService);
+            mService = null;
         }
-        TestUtils.clearAdapterService(mAdapterService);
-        BluetoothMethodProxy.setInstanceForTesting(null);
+        TestUtils.clearAdapterService(mMockAdapterService);
     }
 
+    // *********************************************************************************************
+    // * Initialize Service
+    // *********************************************************************************************
+
     @Test
     public void testInitialize() {
         Assert.assertNotNull(PbapClientService.getPbapClientService());
@@ -115,18 +161,95 @@
         assertThat(PbapClientService.getPbapClientService()).isNull();
     }
 
+    // *********************************************************************************************
+    // * Incoming Events
+    // *********************************************************************************************
+
+    // Account state changes from PbapClientAccountManager
+
     @Test
-    public void dump_callsStateMachineDump() {
+    public void onAccountsChanged_fromNulltoEmpty_tryDownloadIfConnectedCalled() {
         PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
         mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
-        StringBuilder builder = new StringBuilder();
 
-        mService.dump(builder);
+        PbapClientService.PbapClientAccountManagerCallback callback =
+                mService.new PbapClientAccountManagerCallback();
+        callback.onAccountsChanged(null, new ArrayList<Account>());
 
-        verify(sm).dump(builder);
+        verify(sm).tryDownloadIfConnected();
     }
 
     @Test
+    public void onAccountsChanged_fromEmptyToOne_tryDownloadIfConnectedNotCalled() {
+        PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
+        mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
+
+        PbapClientService.PbapClientAccountManagerCallback callback =
+                mService.new PbapClientAccountManagerCallback();
+        Account acc = mock(Account.class);
+        callback.onAccountsChanged(new ArrayList<Account>(), new ArrayList<>(Arrays.asList(acc)));
+
+        verify(sm, never()).tryDownloadIfConnected();
+    }
+
+    // ACL state changes from AdapterService
+
+    @Test
+    public void aclDisconnected_withLeTransport_doesNotCallDisconnect() {
+        int connectionState = BluetoothProfile.STATE_CONNECTED;
+        PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
+        mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
+        when(sm.getConnectionState(mRemoteDevice)).thenReturn(connectionState);
+
+        mService.aclDisconnected(mRemoteDevice, BluetoothDevice.TRANSPORT_LE);
+        TestUtils.waitForLooperToFinishScheduledTask(Looper.getMainLooper());
+
+        verify(sm, never()).disconnect(mRemoteDevice);
+    }
+
+    @Test
+    public void aclDisconnected_withBrEdrTransport_callsDisconnect() {
+        int connectionState = BluetoothProfile.STATE_CONNECTED;
+        PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
+        mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
+        when(sm.getConnectionState(mRemoteDevice)).thenReturn(connectionState);
+
+        mService.aclDisconnected(mRemoteDevice, BluetoothDevice.TRANSPORT_BREDR);
+        TestUtils.waitForLooperToFinishScheduledTask(Looper.getMainLooper());
+
+        verify(sm).disconnect(mRemoteDevice);
+    }
+
+    // HFP HF State changes
+
+    @Test
+    public void headsetClientConnectionStateChanged_hfpCallLogIsRemoved() {
+        mService.handleHeadsetClientConnectionStateChanged(
+                mRemoteDevice,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_DISCONNECTED);
+
+        assertThat(mMockCallLogProvider.getMostRecentlyDeletedDevice())
+                .isEqualTo(mRemoteDevice.getAddress());
+    }
+
+    // Device state machines cleans up
+
+    @Test
+    public void cleanUpDevice() {
+        PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
+        mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
+
+        mService.cleanupDevice(mRemoteDevice);
+
+        assertThat(mService.mPbapClientStateMachineMap).doesNotContainKey(mRemoteDevice);
+    }
+
+    // *********************************************************************************************
+    // * API Methods
+    // *********************************************************************************************
+
+    @Test
     public void testSetConnectionPolicy_withNullDevice_throwsIAE() {
         assertThrows(
                 IllegalArgumentException.class,
@@ -211,171 +334,38 @@
                 .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
     }
 
+    // *********************************************************************************************
+    // * Debug/Dump/toString()
+    // *********************************************************************************************
+
     @Test
-    public void cleanUpDevice() {
+    public void dump_callsStateMachineDump() {
         PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
         mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
+        StringBuilder builder = new StringBuilder();
 
-        mService.cleanupDevice(mRemoteDevice);
+        mService.dump(builder);
 
-        assertThat(mService.mPbapClientStateMachineMap).doesNotContainKey(mRemoteDevice);
+        verify(sm).dump(builder);
     }
 
-    @Test
-    public void getConnectedDevices() {
-        int connectionState = BluetoothProfile.STATE_CONNECTED;
-        PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
-        mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
-        when(sm.getConnectionState()).thenReturn(connectionState);
+    // *********************************************************************************************
+    // * Fake Call Log Provider
+    // *********************************************************************************************
 
-        assertThat(mService.getConnectedDevices()).contains(mRemoteDevice);
-    }
+    private static class MockCallLogProvider extends MockContentProvider {
+        private String mMostRecentlyDeletedDevice = null;
 
-    @Test
-    public void binder_connect_callsServiceMethod() {
-        PbapClientService mockService = mock(PbapClientService.class);
-        PbapClientService.BluetoothPbapClientBinder binder =
-                new PbapClientService.BluetoothPbapClientBinder(mockService);
+        @Override
+        public int delete(Uri uri, String selection, String[] selectionArgs) {
+            if (selectionArgs != null && selectionArgs.length > 0) {
+                mMostRecentlyDeletedDevice = selectionArgs[0];
+            }
+            return 0;
+        }
 
-        binder.connect(mRemoteDevice, null);
-
-        verify(mockService).connect(mRemoteDevice);
-    }
-
-    @Test
-    public void binder_disconnect_callsServiceMethod() {
-        PbapClientService mockService = mock(PbapClientService.class);
-        PbapClientService.BluetoothPbapClientBinder binder =
-                new PbapClientService.BluetoothPbapClientBinder(mockService);
-
-        binder.disconnect(mRemoteDevice, null);
-
-        verify(mockService).disconnect(mRemoteDevice);
-    }
-
-    @Test
-    public void binder_getConnectedDevices_callsServiceMethod() {
-        PbapClientService mockService = mock(PbapClientService.class);
-        PbapClientService.BluetoothPbapClientBinder binder =
-                new PbapClientService.BluetoothPbapClientBinder(mockService);
-
-        binder.getConnectedDevices(null);
-
-        verify(mockService).getConnectedDevices();
-    }
-
-    @Test
-    public void binder_getDevicesMatchingConnectionStates_callsServiceMethod() {
-        PbapClientService mockService = mock(PbapClientService.class);
-        PbapClientService.BluetoothPbapClientBinder binder =
-                new PbapClientService.BluetoothPbapClientBinder(mockService);
-
-        int[] states = new int[] {BluetoothProfile.STATE_CONNECTED};
-        binder.getDevicesMatchingConnectionStates(states, null);
-
-        verify(mockService).getDevicesMatchingConnectionStates(states);
-    }
-
-    @Test
-    public void binder_getConnectionState_callsServiceMethod() {
-        PbapClientService mockService = mock(PbapClientService.class);
-        PbapClientService.BluetoothPbapClientBinder binder =
-                new PbapClientService.BluetoothPbapClientBinder(mockService);
-
-        binder.getConnectionState(mRemoteDevice, null);
-
-        verify(mockService).getConnectionState(mRemoteDevice);
-    }
-
-    @Test
-    public void binder_setConnectionPolicy_callsServiceMethod() {
-        PbapClientService mockService = mock(PbapClientService.class);
-        PbapClientService.BluetoothPbapClientBinder binder =
-                new PbapClientService.BluetoothPbapClientBinder(mockService);
-
-        int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-        binder.setConnectionPolicy(mRemoteDevice, connectionPolicy, null);
-
-        verify(mockService).setConnectionPolicy(mRemoteDevice, connectionPolicy);
-    }
-
-    @Test
-    public void binder_getConnectionPolicy_callsServiceMethod() {
-        PbapClientService mockService = mock(PbapClientService.class);
-        PbapClientService.BluetoothPbapClientBinder binder =
-                new PbapClientService.BluetoothPbapClientBinder(mockService);
-
-        binder.getConnectionPolicy(mRemoteDevice, null);
-
-        verify(mockService).getConnectionPolicy(mRemoteDevice);
-    }
-
-    @Test
-    public void binder_cleanUp_doesNotCrash() {
-        PbapClientService mockService = mock(PbapClientService.class);
-        PbapClientService.BluetoothPbapClientBinder binder =
-                new PbapClientService.BluetoothPbapClientBinder(mockService);
-
-        binder.cleanup();
-    }
-
-    @Test
-    public void broadcastReceiver_withActionAclDisconnectedLeTransport_doesNotCallDisconnect() {
-        int connectionState = BluetoothProfile.STATE_CONNECTED;
-        PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
-        mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
-        when(sm.getConnectionState(mRemoteDevice)).thenReturn(connectionState);
-
-        mService.aclDisconnected(mRemoteDevice, BluetoothDevice.TRANSPORT_LE);
-        TestUtils.waitForLooperToFinishScheduledTask(Looper.getMainLooper());
-
-        verify(sm, never()).disconnect(mRemoteDevice);
-    }
-
-    @Test
-    public void broadcastReceiver_withActionAclDisconnectedBrEdrTransport_callsDisconnect() {
-        int connectionState = BluetoothProfile.STATE_CONNECTED;
-        PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
-        mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
-        when(sm.getConnectionState(mRemoteDevice)).thenReturn(connectionState);
-
-        mService.aclDisconnected(mRemoteDevice, BluetoothDevice.TRANSPORT_BREDR);
-        TestUtils.waitForLooperToFinishScheduledTask(Looper.getMainLooper());
-
-        verify(sm).disconnect(mRemoteDevice);
-    }
-
-    @Test
-    public void broadcastReceiver_withActionUserUnlocked_callsTryDownloadIfConnected() {
-        PbapClientStateMachine sm = mock(PbapClientStateMachine.class);
-        mService.mPbapClientStateMachineMap.put(mRemoteDevice, sm);
-
-        Intent intent = new Intent(Intent.ACTION_USER_UNLOCKED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
-        mService.mPbapBroadcastReceiver.onReceive(mService, intent);
-
-        verify(sm).tryDownloadIfConnected();
-    }
-
-    @Test
-    public void headsetClientConnectionStateChanged_hfpCallLogIsRemoved() {
-        BluetoothMethodProxy methodProxy = spy(BluetoothMethodProxy.getInstance());
-        BluetoothMethodProxy.setInstanceForTesting(methodProxy);
-
-        mService.handleHeadsetClientConnectionStateChanged(
-                mRemoteDevice,
-                BluetoothProfile.STATE_CONNECTED,
-                BluetoothProfile.STATE_DISCONNECTED);
-
-        ArgumentCaptor<Object> selectionArgsCaptor = ArgumentCaptor.forClass(Object.class);
-        verify(methodProxy)
-                .contentResolverDelete(
-                        any(),
-                        eq(CallLog.Calls.CONTENT_URI),
-                        any(),
-                        (String[]) selectionArgsCaptor.capture());
-
-        assertThat(((String[]) selectionArgsCaptor.getValue())[0])
-                .isEqualTo(mRemoteDevice.getAddress());
+        public String getMostRecentlyDeletedDevice() {
+            return mMostRecentlyDeletedDevice;
+        }
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapParserTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapParserTest.java
deleted file mode 100644
index 09a4084..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapParserTest.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.pbapclient;
-
-import android.accounts.Account;
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.bluetooth.TestUtils;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.TimeZone;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class PbapParserTest {
-    private Account mAccount;
-    private Resources mTestResources;
-    private Context mTargetContext;
-    private static final String TEST_ACCOUNT_NAME = "PBAPTESTACCOUNT";
-
-    @Before
-    public void setUp() {
-        mTargetContext = InstrumentationRegistry.getTargetContext();
-        mAccount =
-                new Account(
-                        TEST_ACCOUNT_NAME,
-                        mTargetContext.getString(com.android.bluetooth.R.string.pbap_account_type));
-        mTestResources = TestUtils.getTestApplicationResources(mTargetContext);
-        cleanupCallLog();
-        cleanupPhonebook();
-    }
-
-    // testNoTimestamp should parse 1 poorly formed vcard and not crash.
-    @Test
-    public void testNoTimestamp() throws IOException {
-        InputStream fileStream;
-        fileStream =
-                mTestResources.openRawResource(
-                        com.android.bluetooth.tests.R.raw.no_timestamp_call_log);
-        BluetoothPbapVcardList pbapVCardList =
-                new BluetoothPbapVcardList(
-                        mAccount, fileStream, PbapClientConnectionHandler.VCARD_TYPE_30);
-        Assert.assertEquals(1, pbapVCardList.getCount());
-        CallLogPullRequest processor =
-                new CallLogPullRequest(
-                        mTargetContext,
-                        PbapClientConnectionHandler.MCH_PATH,
-                        new HashMap<>(),
-                        mAccount);
-        processor.setResults(pbapVCardList.getList());
-
-        // Verify that these entries aren't in the call log to start.
-        Assert.assertFalse(verifyCallLog("555-0001", null, "3"));
-
-        // Finish processing the data and verify entries were added to the call log.
-        processor.onPullComplete();
-        Assert.assertTrue(verifyCallLog("555-0001", null, "3"));
-    }
-
-    // testMissedCall should parse one phonecall correctly.
-    @Test
-    public void testMissedCall() throws IOException {
-        InputStream fileStream;
-        fileStream =
-                mTestResources.openRawResource(
-                        com.android.bluetooth.tests.R.raw.single_missed_call);
-        BluetoothPbapVcardList pbapVCardList =
-                new BluetoothPbapVcardList(
-                        mAccount, fileStream, PbapClientConnectionHandler.VCARD_TYPE_30);
-        Assert.assertEquals(1, pbapVCardList.getCount());
-        CallLogPullRequest processor =
-                new CallLogPullRequest(
-                        mTargetContext,
-                        PbapClientConnectionHandler.MCH_PATH,
-                        new HashMap<>(),
-                        mAccount);
-        processor.setResults(pbapVCardList.getList());
-
-        // Verify that these entries aren't in the call log to start.
-        Assert.assertFalse(verifyCallLog("555-0002", "1483232460000", "3"));
-        // Finish processing the data and verify entries were added to the call log.
-        processor.onPullComplete();
-        Assert.assertTrue(verifyCallLog("555-0002", "1483232460000", "3"));
-    }
-
-    // testUnknownCall should parse two calls with no phone number.
-    @Test
-    public void testUnknownCall() throws IOException {
-        InputStream fileStream;
-        fileStream =
-                mTestResources.openRawResource(
-                        com.android.bluetooth.tests.R.raw.unknown_number_call);
-        BluetoothPbapVcardList pbapVCardList =
-                new BluetoothPbapVcardList(
-                        mAccount, fileStream, PbapClientConnectionHandler.VCARD_TYPE_30);
-        Assert.assertEquals(2, pbapVCardList.getCount());
-        CallLogPullRequest processor =
-                new CallLogPullRequest(
-                        mTargetContext,
-                        PbapClientConnectionHandler.MCH_PATH,
-                        new HashMap<>(),
-                        mAccount);
-        processor.setResults(pbapVCardList.getList());
-
-        // Verify that these entries aren't in the call log to start.
-        Assert.assertFalse(verifyCallLog("", "1483232520000", "3"));
-        Assert.assertFalse(verifyCallLog("", "1483232580000", "3"));
-
-        // Finish processing the data and verify entries were added to the call log.
-        processor.onPullComplete();
-        Assert.assertTrue(verifyCallLog("", "1483232520000", "3"));
-        Assert.assertTrue(verifyCallLog("", "1483232580000", "3"));
-    }
-
-    @Test
-    public void testPullPhoneBook() throws IOException {
-        InputStream fileStream;
-        fileStream = mTestResources.openRawResource(com.android.bluetooth.tests.R.raw.v30_simple);
-        BluetoothPbapVcardList pbapVCardList =
-                new BluetoothPbapVcardList(
-                        mAccount, fileStream, PbapClientConnectionHandler.VCARD_TYPE_30);
-        Assert.assertEquals(1, pbapVCardList.getCount());
-        PhonebookPullRequest processor = new PhonebookPullRequest(mTargetContext);
-        processor.setResults(pbapVCardList.getList());
-        Assert.assertFalse(verifyPhonebook("Roid And", "0300000000"));
-        processor.onPullComplete();
-        Assert.assertTrue(verifyPhonebook("Roid And", "0300000000"));
-    }
-
-    private void cleanupCallLog() {
-        mTargetContext.getContentResolver().delete(Calls.CONTENT_URI, null, null);
-    }
-
-    private void cleanupPhonebook() {
-        mTargetContext
-                .getContentResolver()
-                .delete(ContactsContract.RawContacts.CONTENT_URI, null, null);
-    }
-
-    // Find Entries in call log with type matching number and date.
-    // If number or date is null it will match any number or date respectively.
-    private boolean verifyCallLog(String number, String date, String type) {
-        String[] query = new String[] {Calls.NUMBER, Calls.DATE, Calls.TYPE};
-        Cursor cursor =
-                mTargetContext
-                        .getContentResolver()
-                        .query(
-                                Calls.CONTENT_URI,
-                                query,
-                                Calls.TYPE + "= " + type,
-                                null,
-                                Calls.DATE + ", " + Calls.NUMBER);
-        if (date != null) {
-            date = adjDate(date);
-        }
-        if (cursor != null) {
-            while (cursor.moveToNext()) {
-                String foundNumber = cursor.getString(cursor.getColumnIndex(Calls.NUMBER));
-                String foundDate = cursor.getString(cursor.getColumnIndex(Calls.DATE));
-                if ((number == null || number.equals(foundNumber))
-                        && (date == null || date.equals(foundDate))) {
-                    return true;
-                }
-            }
-            cursor.close();
-        }
-        return false;
-    }
-
-    // Get time zone from device and adjust date to the device's time zone.
-    private static String adjDate(String date) {
-        TimeZone tz = TimeZone.getDefault();
-        long dt = Long.valueOf(date) - tz.getRawOffset();
-        return Long.toString(dt);
-    }
-
-    private boolean verifyPhonebook(String name, String number) {
-        Uri uri =
-                Uri.withAppendedPath(
-                        ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
-        Cursor c = mTargetContext.getContentResolver().query(uri, null, null, null);
-        if (c != null && c.getCount() > 0) {
-            c.moveToNext();
-            String displayName =
-                    c.getString(c.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
-            if (displayName.equals(name)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapPhonebookMetadataTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapPhonebookMetadataTest.java
new file mode 100644
index 0000000..ce339d4
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapPhonebookMetadataTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PbapPhonebookMetadataTest {
+    private static final int SIZE = 5;
+    private static final String DATABASE_IDENTIFIER = "dbid";
+    private static final String PRIMARY_VERSION_COUNTER = "pvc";
+    private static final String SECONDARY_VERSION_COUNTER = "svc";
+
+    @Test
+    public void testCreatePhonebookMetadata_forFavorites_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.FAVORITES_PATH,
+                        SIZE,
+                        DATABASE_IDENTIFIER,
+                        PRIMARY_VERSION_COUNTER,
+                        SECONDARY_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.FAVORITES_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier()).isEqualTo(DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter()).isEqualTo(PRIMARY_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter()).isEqualTo(SECONDARY_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testCreatePhonebookMetadata_forLocalPhonebook_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.LOCAL_PHONEBOOK_PATH,
+                        SIZE,
+                        DATABASE_IDENTIFIER,
+                        PRIMARY_VERSION_COUNTER,
+                        SECONDARY_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.LOCAL_PHONEBOOK_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier()).isEqualTo(DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter()).isEqualTo(PRIMARY_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter()).isEqualTo(SECONDARY_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testCreatePhonebookMetadata_forSimLocalPhonebook_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.SIM_PHONEBOOK_PATH,
+                        SIZE,
+                        DATABASE_IDENTIFIER,
+                        PRIMARY_VERSION_COUNTER,
+                        SECONDARY_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.SIM_PHONEBOOK_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier()).isEqualTo(DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter()).isEqualTo(PRIMARY_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter()).isEqualTo(SECONDARY_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testCreatePhonebookMetadata_forIncomingCallHistory_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.ICH_PATH,
+                        SIZE,
+                        PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.ICH_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testCreatePhonebookMetadata_forOutgoingCallHistory_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.OCH_PATH,
+                        SIZE,
+                        PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.OCH_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testCreatePhonebookMetadata_forMissedCallHistory_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.MCH_PATH,
+                        SIZE,
+                        PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.MCH_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testCreatePhonebookMetadata_forSimIncomingCallHistory_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.SIM_ICH_PATH,
+                        SIZE,
+                        PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.SIM_ICH_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testCreatePhonebookMetadata_forSimOutgoingCallHistory_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.SIM_OCH_PATH,
+                        SIZE,
+                        PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.SIM_OCH_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testCreatePhonebookMetadata_forSimMissedCallHistory_metadataCreated() {
+        PbapPhonebookMetadata metadata =
+                new PbapPhonebookMetadata(
+                        PbapPhonebook.SIM_MCH_PATH,
+                        SIZE,
+                        PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER,
+                        PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        assertThat(metadata.getPhonebook()).isEqualTo(PbapPhonebook.SIM_MCH_PATH);
+        assertThat(metadata.getSize()).isEqualTo(SIZE);
+        assertThat(metadata.getDatabaseIdentifier())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+
+        String str = metadata.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapPhonebookTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapPhonebookTest.java
new file mode 100644
index 0000000..36c28fd
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapPhonebookTest.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.accounts.Account;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class PbapPhonebookTest {
+
+    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+    @Mock private Account mMockAccount;
+
+    // *********************************************************************************************
+    // * Create Phonebook
+    // *********************************************************************************************
+
+    @Test
+    public void testCreatePhonebook_forFavorites_emptyFavoritesPhonebookeCreated()
+            throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.FAVORITES_PATH);
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.FAVORITES_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    @Test
+    public void testCreatePhonebook_forLocalPhonebook_emptyLocalPhonebookeCreated()
+            throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.LOCAL_PHONEBOOK_PATH);
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.LOCAL_PHONEBOOK_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    @Test
+    public void testCreatePhonebook_forIncomingCallHistory_emptyIncomingCallHistoryCreated()
+            throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.ICH_PATH);
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.ICH_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    @Test
+    public void testCreatePhonebook_forOutgoingCallHistory_emptyOutgoingCallHistoryCreated()
+            throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.OCH_PATH);
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.OCH_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    @Test
+    public void testCreatePhonebook_forMissedCallHistory_emptyMissedCallHistoryCreated()
+            throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.MCH_PATH);
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.MCH_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    @Test
+    public void testCreatePhonebook_forSimIncomingCallHistory_emptySimIncomingCallHistoryCreated()
+            throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.SIM_ICH_PATH);
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_ICH_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    @Test
+    public void testCreatePhonebook_forSimOutgoingCallHistory_emptySimOutgoingCallHistoryCreated()
+            throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.SIM_OCH_PATH);
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_OCH_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    @Test
+    public void testCreatePhonebook_forSimMissedCallHistory_emptyMiSimssedCallHistoryCreated()
+            throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.SIM_MCH_PATH);
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_MCH_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    // *********************************************************************************************
+    // * Parse Phonebook
+    // *********************************************************************************************
+
+    @Test
+    public void testParsePhonebook_forFavorites_favoritesParsed() throws IOException {
+        String vcard1 =
+                Utils.createVcard(
+                        Utils.VERSION_21,
+                        "Foo",
+                        "Bar",
+                        "+12345678901",
+                        "111 Test Street;Test Town;CA;90210;USA",
+                        "Foo@email.com");
+        String vcard2 =
+                Utils.createVcard(
+                        Utils.VERSION_21,
+                        "Baz",
+                        "Bar",
+                        "+12345678902",
+                        "112 Test Street;Test Town;CA;90210;USA",
+                        "Baz@email.com");
+        String phonebookString =
+                Utils.createPhonebook(Arrays.asList(new String[] {vcard1, vcard2}));
+
+        InputStream stream = toUtf8Stream(phonebookString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.FAVORITES_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.FAVORITES_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParsePhonebook_forLocalPhonebook_localPhonebookParsed() throws IOException {
+        String vcard1 =
+                Utils.createVcard(
+                        Utils.VERSION_21,
+                        "Foo",
+                        "Bar",
+                        "+12345678901",
+                        "111 Test Street;Test Town;CA;90210;USA",
+                        "Foo@email.com");
+        String vcard2 =
+                Utils.createVcard(
+                        Utils.VERSION_21,
+                        "Baz",
+                        "Bar",
+                        "+12345678902",
+                        "112 Test Street;Test Town;CA;90210;USA",
+                        "Baz@email.com");
+        String phonebookString =
+                Utils.createPhonebook(Arrays.asList(new String[] {vcard1, vcard2}));
+
+        InputStream stream = toUtf8Stream(phonebookString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.LOCAL_PHONEBOOK_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.LOCAL_PHONEBOOK_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParsePhonebook_forSimPhonebook_simPhonebookParsed() throws IOException {
+        String vcard1 =
+                Utils.createVcard(
+                        Utils.VERSION_21,
+                        "Foo",
+                        "Bar",
+                        "+12345678901",
+                        "111 Test Street;Test Town;CA;90210;USA",
+                        "Foo@email.com");
+        String vcard2 =
+                Utils.createVcard(
+                        Utils.VERSION_21,
+                        "Baz",
+                        "Bar",
+                        "+12345678902",
+                        "112 Test Street;Test Town;CA;90210;USA",
+                        "Baz@email.com");
+        String phonebookString =
+                Utils.createPhonebook(Arrays.asList(new String[] {vcard1, vcard2}));
+
+        InputStream stream = toUtf8Stream(phonebookString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.SIM_PHONEBOOK_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_PHONEBOOK_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    // *********************************************************************************************
+    // * Parse Call History
+    // *********************************************************************************************
+
+    @Test
+    public void testParsePhonebook_forIncomingCallHistory_incomingCallHistoryParsed()
+            throws IOException {
+        String call1 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.INCOMING_CALL,
+                        "20240101T100000",
+                        "Foo",
+                        "Bar",
+                        "+12345678901");
+        String call2 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.INCOMING_CALL,
+                        "20240101T110000",
+                        "Baz",
+                        "Bar",
+                        "+12345678902");
+        String historyString = Utils.createPhonebook(Arrays.asList(new String[] {call1, call2}));
+
+        InputStream stream = toUtf8Stream(historyString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.ICH_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.ICH_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParsePhonebook_forOutgoingCallHistory_outgoingCallHistoryParsed()
+            throws IOException {
+        String call1 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.OUTGOING_CALL,
+                        "20240101T100000",
+                        "Foo",
+                        "Bar",
+                        "+12345678901");
+        String call2 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.OUTGOING_CALL,
+                        "20240101T110000",
+                        "Baz",
+                        "Bar",
+                        "+12345678902");
+        String historyString = Utils.createPhonebook(Arrays.asList(new String[] {call1, call2}));
+
+        InputStream stream = toUtf8Stream(historyString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.OCH_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.OCH_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParsePhonebook_forMissedCallHistory_missedCallHistoryParsed()
+            throws IOException {
+        String call1 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.MISSED_CALL,
+                        "20240101T100000",
+                        "Foo",
+                        "Bar",
+                        "+12345678901");
+        String call2 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.MISSED_CALL,
+                        "20240101T110000",
+                        "Baz",
+                        "Bar",
+                        "+12345678902");
+        String historyString = Utils.createPhonebook(Arrays.asList(new String[] {call1, call2}));
+
+        InputStream stream = toUtf8Stream(historyString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.MCH_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.MCH_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParsePhonebook_forSimIncomingCallHistory_simIncomingCallHistoryParsed()
+            throws IOException {
+        String call1 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.INCOMING_CALL,
+                        "20240101T100000",
+                        "Foo",
+                        "Bar",
+                        "+12345678901");
+        String call2 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.INCOMING_CALL,
+                        "20240101T110000",
+                        "Baz",
+                        "Bar",
+                        "+12345678902");
+        String historyString = Utils.createPhonebook(Arrays.asList(new String[] {call1, call2}));
+
+        InputStream stream = toUtf8Stream(historyString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.SIM_ICH_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_ICH_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParsePhonebook_forSimOutgoingCallHistory_simOutgoingCallHistoryParsed()
+            throws IOException {
+        String call1 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.OUTGOING_CALL,
+                        "20240101T100000",
+                        "Foo",
+                        "Bar",
+                        "+12345678901");
+        String call2 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.OUTGOING_CALL,
+                        "20240101T110000",
+                        "Baz",
+                        "Bar",
+                        "+12345678902");
+        String historyString = Utils.createPhonebook(Arrays.asList(new String[] {call1, call2}));
+
+        InputStream stream = toUtf8Stream(historyString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.SIM_OCH_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_OCH_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParsePhonebook_forSimMissedCallHistory_simMissedCallHistoryParsed()
+            throws IOException {
+        String call1 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.MISSED_CALL,
+                        "20240101T100000",
+                        "Foo",
+                        "Bar",
+                        "+12345678901");
+        String call2 =
+                Utils.createCallHistory(
+                        Utils.VERSION_21,
+                        Utils.MISSED_CALL,
+                        "20240101T110000",
+                        "Baz",
+                        "Bar",
+                        "+12345678902");
+        String historyString = Utils.createPhonebook(Arrays.asList(new String[] {call1, call2}));
+
+        InputStream stream = toUtf8Stream(historyString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.SIM_MCH_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_MCH_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    // *********************************************************************************************
+    // * Parsing Edge Cases
+    // *********************************************************************************************
+
+    @Test
+    public void testParse21Phonebook_reportedAs30_parsedCorrectly() throws IOException {
+        String vcard1 =
+                Utils.createVcard(
+                        Utils.VERSION_21,
+                        "Foo",
+                        "Bar",
+                        "+12345678901",
+                        "111 Test Street;Test Town;CA;90210;USA",
+                        "Foo@email.com");
+        String vcard2 =
+                Utils.createVcard(
+                        Utils.VERSION_21,
+                        "Baz",
+                        "Bar",
+                        "+12345678902",
+                        "112 Test Street;Test Town;CA;90210;USA",
+                        "Baz@email.com");
+        String phonebookString =
+                Utils.createPhonebook(Arrays.asList(new String[] {vcard1, vcard2}));
+
+        InputStream stream = toUtf8Stream(phonebookString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.SIM_PHONEBOOK_PATH,
+                        PbapPhonebook.FORMAT_VCARD_30,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_PHONEBOOK_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParse30Phonebook_reportedAs21_parsedCorrectly() throws IOException {
+        String vcard1 =
+                Utils.createVcard(
+                        Utils.VERSION_30,
+                        "Foo",
+                        "Bar",
+                        "+12345678901",
+                        "111 Test Street;Test Town;CA;90210;USA",
+                        "Foo@email.com");
+        String vcard2 =
+                Utils.createVcard(
+                        Utils.VERSION_30,
+                        "Baz",
+                        "Bar",
+                        "+12345678902",
+                        "112 Test Street;Test Town;CA;90210;USA",
+                        "Baz@email.com");
+        String phonebookString =
+                Utils.createPhonebook(Arrays.asList(new String[] {vcard1, vcard2}));
+
+        InputStream stream = toUtf8Stream(phonebookString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.SIM_PHONEBOOK_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_PHONEBOOK_PATH);
+        assertThat(phonebook.getCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void testParseUnsupportedPhonebook_reportedAs21_parsingFails() throws IOException {
+        String vcard1 =
+                Utils.createVcard(
+                        Utils.VERSION_UNSUPPORTED,
+                        "Foo",
+                        "Bar",
+                        "+12345678901",
+                        "111 Test Street;Test Town;CA;90210;USA",
+                        "PORTED@email.com");
+        String vcard2 =
+                Utils.createVcard(
+                        Utils.VERSION_UNSUPPORTED,
+                        "Baz",
+                        "Bar",
+                        "+12345678902",
+                        "112 Test Street;Test Town;CA;90210;USA",
+                        "PORTED@email.com");
+        String phonebookString =
+                Utils.createPhonebook(Arrays.asList(new String[] {vcard1, vcard2}));
+
+        InputStream stream = toUtf8Stream(phonebookString);
+        PbapPhonebook phonebook =
+                new PbapPhonebook(
+                        PbapPhonebook.SIM_PHONEBOOK_PATH,
+                        PbapPhonebook.FORMAT_VCARD_21,
+                        0,
+                        mMockAccount,
+                        stream);
+
+        assertThat(phonebook.getPhonebook()).isEqualTo(PbapPhonebook.SIM_PHONEBOOK_PATH);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isNotNull();
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    // *********************************************************************************************
+    // * Debug/Dump/toString()
+    // *********************************************************************************************
+
+    @Test
+    public void testPhonebookToString() throws IOException {
+        PbapPhonebook phonebook = new PbapPhonebook(PbapPhonebook.LOCAL_PHONEBOOK_PATH);
+        String str = phonebook.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    // *********************************************************************************************
+    // * Utilities
+    // *********************************************************************************************
+
+    private InputStream toUtf8Stream(String s) {
+        return new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8));
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapSdpRecordTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapSdpRecordTest.java
new file mode 100644
index 0000000..d61ca70
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapSdpRecordTest.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.SdpPseRecord;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.TestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PbapSdpRecordTest {
+    private BluetoothAdapter mAdapter;
+    private BluetoothDevice mTestDevice;
+
+    private static final String SERVICE_NAME = "PSE SERVICE NAME";
+    private static final int L2CAP_PSM = 4101;
+    private static final int RFCOMM_CHANNEL = 5;
+    private static final int INVALID_L2CAP = -1;
+    private static final int INVALID_RFCOMM = -1;
+
+    private static final int SUPPORTED_REPOSITORIES =
+            PbapSdpRecord.REPOSITORY_LOCAL_PHONEBOOK
+                    | PbapSdpRecord.REPOSITORY_SIM_CARD
+                    | PbapSdpRecord.REPOSITORY_SPEED_DIAL
+                    | PbapSdpRecord.REPOSITORY_FAVORITES;
+
+    private static final int SUPPORTED_FEATURES =
+            PbapSdpRecord.FEATURE_DOWNLOADING
+                    | PbapSdpRecord.FEATURE_BROWSING
+                    | PbapSdpRecord.FEATURE_DATABASE_IDENTIFIER
+                    | PbapSdpRecord.FEATURE_FOLDER_VERSION_COUNTERS
+                    | PbapSdpRecord.FEATURE_VCARD_SELECTING
+                    | PbapSdpRecord.FEATURE_ENHANCED_MISSED_CALLS
+                    | PbapSdpRecord.FEATURE_XBT_UCI_VCARD_PROPERTY
+                    | PbapSdpRecord.FEATURE_XBT_UID_VCARD_PROPERTY
+                    | PbapSdpRecord.FEATURE_CONTACT_REFERENCING
+                    | PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT;
+
+    // For utility function testing-- -1 is FIELD_MISSING, and other negatives should be unknown
+    private static final int UNRECOGNIZED = -2;
+
+    @Before
+    public void setUp() throws Exception {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mTestDevice = TestUtils.getTestDevice(mAdapter, 1);
+    }
+
+    @Test
+    public void testMakeWithDevice() {
+        PbapSdpRecord record =
+                makeSdpRecord(INVALID_L2CAP, INVALID_RFCOMM, PbapSdpRecord.VERSION_1_0, 0, 0);
+        assertThat(record.getDevice()).isEqualTo(mTestDevice);
+    }
+
+    @Test
+    public void testMakeWithServiceName() {
+        PbapSdpRecord record =
+                makeSdpRecord(INVALID_L2CAP, INVALID_RFCOMM, PbapSdpRecord.VERSION_1_0, 0, 0);
+        assertThat(record.getServiceName()).isEqualTo(SERVICE_NAME);
+    }
+
+    @Test
+    public void testMakeVersion10() {
+        PbapSdpRecord record =
+                makeSdpRecord(INVALID_L2CAP, INVALID_RFCOMM, PbapSdpRecord.VERSION_1_0, 0, 0);
+        assertThat(record.getProfileVersion()).isEqualTo(PbapSdpRecord.VERSION_1_0);
+    }
+
+    @Test
+    public void testMakeVersion11() {
+        PbapSdpRecord record =
+                makeSdpRecord(INVALID_L2CAP, INVALID_RFCOMM, PbapSdpRecord.VERSION_1_1, 0, 0);
+        assertThat(record.getProfileVersion()).isEqualTo(PbapSdpRecord.VERSION_1_1);
+    }
+
+    @Test
+    public void testMakeVersion12() {
+        PbapSdpRecord record =
+                makeSdpRecord(INVALID_L2CAP, INVALID_RFCOMM, PbapSdpRecord.VERSION_1_2, 0, 0);
+        assertThat(record.getProfileVersion()).isEqualTo(PbapSdpRecord.VERSION_1_2);
+    }
+
+    @Test
+    public void testMakeL2capTransport() {
+        PbapSdpRecord record =
+                makeSdpRecord(L2CAP_PSM, INVALID_RFCOMM, PbapSdpRecord.VERSION_1_2, 0, 0);
+        assertThat(record.getL2capPsm()).isEqualTo(L2CAP_PSM);
+        assertThat(record.getRfcommChannelNumber()).isEqualTo(INVALID_RFCOMM);
+    }
+
+    @Test
+    public void testMakeRfcommTransport() {
+        PbapSdpRecord record =
+                makeSdpRecord(INVALID_L2CAP, RFCOMM_CHANNEL, PbapSdpRecord.VERSION_1_2, 0, 0);
+        assertThat(record.getL2capPsm()).isEqualTo(INVALID_L2CAP);
+        assertThat(record.getRfcommChannelNumber()).isEqualTo(RFCOMM_CHANNEL);
+    }
+
+    @Test
+    public void testMakeBothTransport() {
+        PbapSdpRecord record =
+                makeSdpRecord(L2CAP_PSM, RFCOMM_CHANNEL, PbapSdpRecord.VERSION_1_2, 0, 0);
+        assertThat(record.getL2capPsm()).isEqualTo(L2CAP_PSM);
+        assertThat(record.getRfcommChannelNumber()).isEqualTo(RFCOMM_CHANNEL);
+    }
+
+    @Test
+    public void testSupportedFeature_featureDownloading() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_DOWNLOADING,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_DOWNLOADING)).isTrue();
+        assertThat(record.getSupportedFeatures()).isEqualTo(PbapSdpRecord.FEATURE_DOWNLOADING);
+    }
+
+    @Test
+    public void testSupportedFeature_featureBrowsing() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_BROWSING,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_BROWSING)).isTrue();
+        assertThat(record.getSupportedFeatures()).isEqualTo(PbapSdpRecord.FEATURE_BROWSING);
+    }
+
+    @Test
+    public void testSupportedFeature_featureDatabaseIdentifier() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_DATABASE_IDENTIFIER,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_DATABASE_IDENTIFIER)).isTrue();
+        assertThat(record.getSupportedFeatures())
+                .isEqualTo(PbapSdpRecord.FEATURE_DATABASE_IDENTIFIER);
+    }
+
+    @Test
+    public void testSupportedFeature_featureFolderVersionCounters() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_FOLDER_VERSION_COUNTERS,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_FOLDER_VERSION_COUNTERS))
+                .isTrue();
+        assertThat(record.getSupportedFeatures())
+                .isEqualTo(PbapSdpRecord.FEATURE_FOLDER_VERSION_COUNTERS);
+    }
+
+    @Test
+    public void testSupportedFeature_featureVcardSelecting() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_VCARD_SELECTING,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_VCARD_SELECTING)).isTrue();
+        assertThat(record.getSupportedFeatures()).isEqualTo(PbapSdpRecord.FEATURE_VCARD_SELECTING);
+    }
+
+    @Test
+    public void testSupportedFeature_featureEnhancedMissedCalls() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_ENHANCED_MISSED_CALLS,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_ENHANCED_MISSED_CALLS)).isTrue();
+        assertThat(record.getSupportedFeatures())
+                .isEqualTo(PbapSdpRecord.FEATURE_ENHANCED_MISSED_CALLS);
+    }
+
+    @Test
+    public void testSupportedFeature_featureXbtUciVcardProperty() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_XBT_UCI_VCARD_PROPERTY,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_XBT_UCI_VCARD_PROPERTY))
+                .isTrue();
+        assertThat(record.getSupportedFeatures())
+                .isEqualTo(PbapSdpRecord.FEATURE_XBT_UCI_VCARD_PROPERTY);
+    }
+
+    @Test
+    public void testSupportedFeature_featureXbtUidVcardProperty() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_XBT_UID_VCARD_PROPERTY,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_XBT_UID_VCARD_PROPERTY))
+                .isTrue();
+        assertThat(record.getSupportedFeatures())
+                .isEqualTo(PbapSdpRecord.FEATURE_XBT_UID_VCARD_PROPERTY);
+    }
+
+    @Test
+    public void testSupportedFeature_featureContactReferencing() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_CONTACT_REFERENCING,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_CONTACT_REFERENCING)).isTrue();
+        assertThat(record.getSupportedFeatures())
+                .isEqualTo(PbapSdpRecord.FEATURE_CONTACT_REFERENCING);
+    }
+
+    @Test
+    public void testSupportedFeature_featureDefaultImageFormat() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT)).isTrue();
+        assertThat(record.getSupportedFeatures())
+                .isEqualTo(PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT);
+    }
+
+    @Test
+    public void testSupportedFeatures_allFeaturesSupported() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        SUPPORTED_FEATURES,
+                        0);
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_DOWNLOADING)).isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_BROWSING)).isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_DATABASE_IDENTIFIER)).isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_FOLDER_VERSION_COUNTERS))
+                .isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_VCARD_SELECTING)).isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_ENHANCED_MISSED_CALLS)).isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_XBT_UCI_VCARD_PROPERTY))
+                .isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_XBT_UID_VCARD_PROPERTY))
+                .isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_CONTACT_REFERENCING)).isTrue();
+        assertThat(record.isFeatureSupported(PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT)).isTrue();
+        assertThat(record.getSupportedFeatures()).isEqualTo(SUPPORTED_FEATURES);
+    }
+
+    @Test
+    public void testSupportedRepository_repositoryLocalPhonebook() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        0,
+                        PbapSdpRecord.REPOSITORY_LOCAL_PHONEBOOK);
+        assertThat(record.isRepositorySupported(PbapSdpRecord.REPOSITORY_LOCAL_PHONEBOOK)).isTrue();
+        assertThat(record.getSupportedRepositories())
+                .isEqualTo(PbapSdpRecord.REPOSITORY_LOCAL_PHONEBOOK);
+    }
+
+    @Test
+    public void testSupportedRepository_repositorySimCard() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        0,
+                        PbapSdpRecord.REPOSITORY_SIM_CARD);
+        assertThat(record.isRepositorySupported(PbapSdpRecord.REPOSITORY_SIM_CARD)).isTrue();
+        assertThat(record.getSupportedRepositories()).isEqualTo(PbapSdpRecord.REPOSITORY_SIM_CARD);
+    }
+
+    @Test
+    public void testSupportedRepository_repositorySpeedDial() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        0,
+                        PbapSdpRecord.REPOSITORY_SPEED_DIAL);
+        assertThat(record.isRepositorySupported(PbapSdpRecord.REPOSITORY_SPEED_DIAL)).isTrue();
+        assertThat(record.getSupportedRepositories())
+                .isEqualTo(PbapSdpRecord.REPOSITORY_SPEED_DIAL);
+    }
+
+    @Test
+    public void testSupportedRepository_repositoryFavorites() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        0,
+                        PbapSdpRecord.REPOSITORY_FAVORITES);
+        assertThat(record.isRepositorySupported(PbapSdpRecord.REPOSITORY_FAVORITES)).isTrue();
+        assertThat(record.getSupportedRepositories()).isEqualTo(PbapSdpRecord.REPOSITORY_FAVORITES);
+    }
+
+    @Test
+    public void testSupportedRepositories() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        0,
+                        SUPPORTED_REPOSITORIES);
+        assertThat(record.isRepositorySupported(PbapSdpRecord.REPOSITORY_LOCAL_PHONEBOOK)).isTrue();
+        assertThat(record.isRepositorySupported(PbapSdpRecord.REPOSITORY_SIM_CARD)).isTrue();
+        assertThat(record.isRepositorySupported(PbapSdpRecord.REPOSITORY_SPEED_DIAL)).isTrue();
+        assertThat(record.isRepositorySupported(PbapSdpRecord.REPOSITORY_FAVORITES)).isTrue();
+        assertThat(record.getSupportedRepositories()).isEqualTo(SUPPORTED_REPOSITORIES);
+    }
+
+    @Test
+    @SuppressLint("UnusedVariable")
+    public void testMakeWithNullDevice() {
+        assertThrows(
+                NullPointerException.class,
+                () -> {
+                    PbapSdpRecord record =
+                            new PbapSdpRecord(null, new SdpPseRecord(0, 0, 0, 0, 0, ""));
+                });
+    }
+
+    @Test
+    @SuppressLint("UnusedVariable")
+    public void testMakeWithNullRecord() {
+        assertThrows(
+                NullPointerException.class,
+                () -> {
+                    PbapSdpRecord record = new PbapSdpRecord(mTestDevice, null);
+                });
+    }
+
+    @Test
+    public void testRecordToString() {
+        PbapSdpRecord record =
+                makeSdpRecord(
+                        L2CAP_PSM,
+                        RFCOMM_CHANNEL,
+                        PbapSdpRecord.VERSION_1_2,
+                        0,
+                        PbapSdpRecord.REPOSITORY_FAVORITES);
+        String str = record.toString();
+        assertThat(str).isNotNull();
+        assertThat(str.length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void testVersionToStringUtility() {
+        assertThat(PbapSdpRecord.versionToString(PbapSdpRecord.VERSION_1_0)).isNotEmpty();
+        assertThat(PbapSdpRecord.versionToString(PbapSdpRecord.VERSION_1_1)).isNotEmpty();
+        assertThat(PbapSdpRecord.versionToString(PbapSdpRecord.VERSION_1_2)).isNotEmpty();
+        assertThat(PbapSdpRecord.versionToString(PbapSdpRecord.FIELD_MISSING)).isNotEmpty();
+        assertThat(PbapSdpRecord.versionToString(UNRECOGNIZED)).isNotEmpty();
+    }
+
+    @Test
+    public void testFeatureToStringUtility() {
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_DOWNLOADING)).isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_BROWSING)).isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_DATABASE_IDENTIFIER))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_FOLDER_VERSION_COUNTERS))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_VCARD_SELECTING))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_ENHANCED_MISSED_CALLS))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_XBT_UCI_VCARD_PROPERTY))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_XBT_UID_VCARD_PROPERTY))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_CONTACT_REFERENCING))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.featureToString(UNRECOGNIZED)).isNotEmpty();
+    }
+
+    @Test
+    public void testRepositoryToStringUtility() {
+        assertThat(PbapSdpRecord.repositoryToString(PbapSdpRecord.REPOSITORY_LOCAL_PHONEBOOK))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.repositoryToString(PbapSdpRecord.REPOSITORY_SIM_CARD))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.repositoryToString(PbapSdpRecord.REPOSITORY_SPEED_DIAL))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.repositoryToString(PbapSdpRecord.REPOSITORY_FAVORITES))
+                .isNotEmpty();
+        assertThat(PbapSdpRecord.repositoryToString(UNRECOGNIZED)).isNotEmpty();
+    }
+
+    // *********************************************************************************************
+    // * Test Utilities
+    // *********************************************************************************************
+
+    private PbapSdpRecord makeSdpRecord(
+            int l2capPsm, int rfcommChnl, int version, int features, int repositories) {
+        SdpPseRecord sdpRecord =
+                new SdpPseRecord(
+                        l2capPsm, rfcommChnl, version, features, repositories, SERVICE_NAME);
+        return new PbapSdpRecord(mTestDevice, sdpRecord);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/RequestPullPhonebookMetadataTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/RequestPullPhonebookMetadataTest.java
new file mode 100644
index 0000000..5f50e76
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/RequestPullPhonebookMetadataTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.FakeObexServer;
+import com.android.obex.ApplicationParameter;
+import com.android.obex.ClientSession;
+import com.android.obex.HeaderSet;
+import com.android.obex.Operation;
+import com.android.obex.ResponseCodes;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+@RunWith(AndroidJUnit4.class)
+public class RequestPullPhonebookMetadataTest {
+    private static final String PHONEBOOK_NAME = "phonebook";
+    private static final short PHONEBOOK_SIZE = 200;
+
+    private FakePbapObexServer mServer;
+    private ClientSession mSession;
+    private RequestPullPhonebookMetadata mRequest;
+
+    @Before
+    public void setUp() throws IOException {
+        mServer = new FakePbapObexServer();
+        mSession = mServer.getClientSession();
+
+        PbapApplicationParameters params =
+                new PbapApplicationParameters(
+                        PbapApplicationParameters.PROPERTIES_ALL,
+                        PbapPhonebook.FORMAT_VCARD_30,
+                        PbapApplicationParameters.MAX_PHONEBOOK_SIZE,
+                        /* startOffset= */ 0);
+        mRequest = new RequestPullPhonebookMetadata(PHONEBOOK_NAME, params);
+    }
+
+    @Test
+    public void getType_returnsTypeMetadataRequest() {
+        assertThat(mRequest.getType()).isEqualTo(PbapClientRequest.TYPE_PULL_PHONEBOOK_METADATA);
+    }
+
+    @Test
+    public void getResponseCode_beforeExecutingRequest_returnsNegativeOne() {
+        assertThat(mRequest.getResponseCode()).isEqualTo(-1);
+    }
+
+    @Test
+    public void execute_sessionConnectedAndResponseOk_returnsMetadata() throws IOException {
+        mSession.connect(null);
+        mServer.setSize(PHONEBOOK_SIZE);
+
+        mRequest.execute(mSession);
+
+        assertThat(mRequest.getResponseCode()).isEqualTo(ResponseCodes.OBEX_HTTP_OK);
+        assertThat(mRequest.getPhonebook()).isEqualTo(PHONEBOOK_NAME);
+
+        PbapPhonebookMetadata metadata = mRequest.getMetadata();
+        assertThat(metadata.getPhonebook()).isEqualTo(PHONEBOOK_NAME);
+        assertThat(metadata.getSize()).isEqualTo(200);
+        assertThat(metadata.getDatabaseIdentifier())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+    }
+
+    @Test
+    public void execute_sessionConnectedAndResponseBad_returnsEmptyMetadata() throws IOException {
+        mSession.connect(null);
+        mServer.setResponseCode(ResponseCodes.OBEX_HTTP_BAD_REQUEST);
+        mRequest.execute(mSession);
+
+        assertThat(mRequest.getResponseCode()).isEqualTo(ResponseCodes.OBEX_HTTP_BAD_REQUEST);
+        assertThat(mRequest.getPhonebook()).isEqualTo(PHONEBOOK_NAME);
+
+        PbapPhonebookMetadata metadata = mRequest.getMetadata();
+        assertThat(metadata.getPhonebook()).isEqualTo(PHONEBOOK_NAME);
+        assertThat(metadata.getSize()).isEqualTo(PbapPhonebookMetadata.INVALID_SIZE);
+        assertThat(metadata.getDatabaseIdentifier())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_DATABASE_IDENTIFIER);
+        assertThat(metadata.getPrimaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+        assertThat(metadata.getSecondaryVersionCounter())
+                .isEqualTo(PbapPhonebookMetadata.INVALID_VERSION_COUNTER);
+    }
+
+    @Test
+    public void execute_sessionNotConnected_throwsIOException() throws IOException {
+        assertThrows(IOException.class, () -> mRequest.execute(mSession));
+        assertThat(mRequest.getResponseCode()).isEqualTo(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR);
+    }
+
+    @Test
+    public void readResponseHeaders() {
+        try {
+            HeaderSet headerSet = new HeaderSet();
+            mRequest.readResponseHeaders(headerSet);
+            assertThat(mRequest.getMetadata().getSize())
+                    .isEqualTo(PbapPhonebookMetadata.INVALID_SIZE);
+        } catch (Exception e) {
+            assertWithMessage("Exception should not happen.").fail();
+        }
+    }
+
+    // *********************************************************************************************
+    // * Fake PBAP Server
+    // *********************************************************************************************
+
+    private static class FakePbapObexServer extends FakeObexServer {
+        private static final byte SIZE_BYTES = 2;
+
+        private int mResponseCode = ResponseCodes.OBEX_HTTP_OK;
+        private short mSize = 0;
+
+        FakePbapObexServer() throws IOException {
+            super();
+        }
+
+        public void setResponseCode(int responseCode) {
+            mResponseCode = responseCode;
+        }
+
+        public void setSize(short size) {
+            mSize = size;
+        }
+
+        @Override
+        public int onGet(final Operation op) {
+            if (mResponseCode != ResponseCodes.OBEX_HTTP_OK) {
+                return mResponseCode;
+            }
+
+            ApplicationParameter params = new ApplicationParameter();
+            params.addTriplet(
+                    PbapApplicationParameters.OAP_PHONEBOOK_SIZE,
+                    SIZE_BYTES,
+                    shortToByteArray(mSize));
+
+            HeaderSet replyHeaders = new HeaderSet();
+            replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, params.getHeader());
+            return sendResponse(op, replyHeaders, null);
+        }
+
+        public byte[] shortToByteArray(short s) {
+            ByteBuffer ret = ByteBuffer.allocate(2);
+            ret.putShort(s);
+            return ret.array();
+        }
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/RequestPullPhonebookTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/RequestPullPhonebookTest.java
new file mode 100644
index 0000000..d70060d
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/RequestPullPhonebookTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.accounts.Account;
+import android.util.Log;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.FakeObexServer;
+import com.android.obex.ClientSession;
+import com.android.obex.HeaderSet;
+import com.android.obex.Operation;
+import com.android.obex.ResponseCodes;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class RequestPullPhonebookTest {
+
+    private static final String PHONEBOOK_NAME = "phonebook";
+    private static final Account ACCOUNT = mock(Account.class);
+
+    private FakePbapObexServer mServer;
+    private ClientSession mSession;
+
+    private RequestPullPhonebook mRequest;
+
+    @Before
+    public void setUp() throws IOException {
+        mServer = new FakePbapObexServer();
+        mSession = mServer.getClientSession();
+
+        PbapApplicationParameters params =
+                new PbapApplicationParameters(
+                        PbapApplicationParameters.PROPERTIES_ALL,
+                        PbapPhonebook.FORMAT_VCARD_30,
+                        PbapApplicationParameters.MAX_PHONEBOOK_SIZE,
+                        /* startOffset= */ 0);
+        mRequest = new RequestPullPhonebook(PHONEBOOK_NAME, params, ACCOUNT);
+    }
+
+    @Test
+    public void getType_returnsTypeMetadataRequest() {
+        assertThat(mRequest.getType()).isEqualTo(PbapClientRequest.TYPE_PULL_PHONEBOOK);
+    }
+
+    @Test
+    public void getResponseCode_beforeExecutingRequest_returnsNegativeOne() {
+        assertThat(mRequest.getResponseCode()).isEqualTo(-1);
+    }
+
+    @Test
+    public void executeRequest_sessionConnectedWithContacts_returnsContacts() throws IOException {
+        mSession.connect(null);
+
+        String vcard =
+                Utils.createVcard(
+                        Utils.VERSION_30,
+                        "Foo",
+                        "Bar",
+                        "+1-234-567-8901",
+                        "111 Test Street;Test Town;CA;90210;USA",
+                        "Foo@email.com");
+        mServer.addContact(vcard);
+
+        mRequest.execute(mSession);
+
+        assertThat(mRequest.getResponseCode()).isEqualTo(ResponseCodes.OBEX_HTTP_OK);
+        assertThat(mRequest.getPhonebook()).isEqualTo(PHONEBOOK_NAME);
+
+        PbapPhonebook phonebook = mRequest.getContacts();
+        assertThat(phonebook).isNotNull();
+        assertThat(phonebook.getPhonebook()).isEqualTo(PHONEBOOK_NAME);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(1);
+        assertThat(phonebook.getList()).isNotEmpty();
+    }
+
+    @Test
+    public void execute_sessionConnectedAndResponseBad_returnsEmptyPhonebook() throws IOException {
+        mSession.connect(null);
+        mServer.setResponseCode(ResponseCodes.OBEX_HTTP_BAD_REQUEST);
+
+        mRequest.execute(mSession);
+
+        assertThat(mRequest.getResponseCode()).isEqualTo(ResponseCodes.OBEX_HTTP_BAD_REQUEST);
+        assertThat(mRequest.getPhonebook()).isEqualTo(PHONEBOOK_NAME);
+
+        PbapPhonebook phonebook = mRequest.getContacts();
+        assertThat(phonebook).isNotNull();
+        assertThat(phonebook.getPhonebook()).isEqualTo(PHONEBOOK_NAME);
+        assertThat(phonebook.getOffset()).isEqualTo(0);
+        assertThat(phonebook.getCount()).isEqualTo(0);
+        assertThat(phonebook.getList()).isEmpty();
+    }
+
+    // *********************************************************************************************
+    // * Fake PBAP Server
+    // *********************************************************************************************
+
+    private static class FakePbapObexServer extends FakeObexServer {
+        private static final String TAG = FakePbapObexServer.class.getSimpleName();
+
+        private int mResponseCode = ResponseCodes.OBEX_HTTP_OK;
+        private final List<String> mPhonebook = new ArrayList<>();
+
+        FakePbapObexServer() throws IOException {
+            super();
+        }
+
+        public void setResponseCode(int responseCode) {
+            mResponseCode = responseCode;
+        }
+
+        public void addContact(String vcard) {
+            mPhonebook.add(vcard);
+        }
+
+        @Override
+        public int onGet(final Operation op) {
+            Log.i(TAG, "onGet()");
+
+            if (mResponseCode != ResponseCodes.OBEX_HTTP_OK) {
+                return mResponseCode;
+            }
+
+            byte[] contacts = null;
+            if (mPhonebook.size() > 0) {
+                String phonebook = Utils.createPhonebook(mPhonebook);
+                contacts = phonebook.getBytes();
+            }
+
+            HeaderSet replyHeaders = new HeaderSet();
+            return sendResponse(op, replyHeaders, contacts);
+        }
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/Utils.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/Utils.java
new file mode 100644
index 0000000..fd97857
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/Utils.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import java.util.List;
+
+public class Utils {
+    // VCard Version strings
+    public static final String VERSION_21 = "2.1";
+    public static final String VERSION_30 = "3.0";
+    public static final String VERSION_UNSUPPORTED = "4.0";
+
+    // Constants for creating VCard strings.
+    private static final String N = "N";
+    private static final String FN = "FN";
+    private static final String ADDR = "ADR;TYPE=HOME";
+    private static final String CELL = "TEL;TYPE=CELL";
+    private static final String EMAIL = "EMAIL;INTERNET";
+    private static final String TEL = "TEL;TYPE=0";
+
+    // Constants for creating a call history entry
+    public static final String MISSED_CALL = "MISSED";
+    public static final String INCOMING_CALL = "RECEIVED";
+    public static final String OUTGOING_CALL = "DIALED";
+    private static final String CALL_HISTORY = "X-IRMC-CALL-DATETIME";
+
+    public static final String ACCOUNT_TYPE =
+            "com.android.bluetooth.pbapclient";
+
+    /**
+     * Group a list of VCard entries or Call History entries into a full phonebook
+     *
+     * @param vcardStrings The list of VCard or call history strings to group into a phonebook
+     * @return A string representation of the entire phonebook
+     */
+    public static String createPhonebook(List<String> vcardStrings) {
+        StringBuilder sb = new StringBuilder();
+        for (String vcard : vcardStrings) {
+            sb.append(vcard).append("\n");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Create a VCard string fit for parsing
+     *
+     * <p>A null value in any field outside of name fields will cause it to be dropped from the
+     * entry.
+     *
+     * @param version the version of the VCard you want to create
+     * @param first the first name of the VCard you want to create
+     * @param last the last name of the VCard you want to create
+     * @param phone the phone number of the VCard you want to create
+     * @param addr the address of the VCard you want to create
+     * @param email the email of the VCard you want to create
+     * @return a VCard string, built with the information provided
+     */
+    public static String createVcard(
+            String version, String first, String last, String phone, String addr, String email) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("BEGIN:VCARD\n");
+        sb.append("VERSION:").append(version).append("\n");
+
+        // Friendly name
+        sb.append(FN).append(":").append(first).append(" ").append(last).append("\n");
+
+        // Full name: “LastName;FirstName;MiddleName;Prefix;Suffix”
+        sb.append(N).append(":").append(last).append(";").append(first).append("\n");
+
+        if (phone != null) {
+            sb.append(CELL).append(":").append(phone).append("\n");
+        }
+
+        if (addr != null) {
+            sb.append(ADDR).append(":").append(addr).append("\n");
+        }
+
+        if (email != null) {
+            sb.append(EMAIL).append(":").append(email).append("\n");
+        }
+
+        sb.append("END:VCARD");
+
+        return sb.toString();
+    }
+
+    /**
+     * Create a call history entry string fit for parsing
+     *
+     * <p>A call history entry is a VCard with special fields to carry the type of call and the time
+     * the call occurred
+     *
+     * @param version the version of the call history entry you want to create
+     * @param type the type of the call history entry you want to create
+     * @param time the time of the call history entry, in the format "YYYYMMDDTHHMMSS"
+     * @param first the first name of the person who was called
+     * @param last the last name of the person who was called
+     * @param phone the phone number of the person who was called
+     */
+    public static String createCallHistory(
+            String version, String type, String time, String first, String last, String phone) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("BEGIN:VCARD\n");
+        sb.append("VERSION:").append(version).append("\n");
+
+        if (VERSION_30.equals(version)) {
+            sb.append(FN).append(":").append(first).append(" ").append(last).append("\n");
+        }
+
+        sb.append(N).append(":").append(last).append(";").append(first).append("\n");
+
+        sb.append(TEL).append(":").append(phone).append("\n");
+
+        // Time format: YYYYMMDDTHHMMSS -> 20050320T100000
+        if (VERSION_30.equals(version)) {
+            sb.append(CALL_HISTORY)
+                    .append(";TYPE=")
+                    .append(type)
+                    .append(":")
+                    .append(time)
+                    .append("\n");
+        } else {
+            sb.append(CALL_HISTORY).append(";").append(type).append(":").append(time).append("\n");
+        }
+
+        sb.append("END:VCARD");
+
+        return sb.toString();
+    }
+}
diff --git a/flags/gap.aconfig b/flags/gap.aconfig
index e3da97a..646e844 100644
--- a/flags/gap.aconfig
+++ b/flags/gap.aconfig
@@ -30,13 +30,6 @@
 }
 
 flag {
-    name: "gatt_cleanup_restricted_handles"
-    namespace: "bluetooth"
-    description: "Cleans up restricted handles when disconnected"
-    bug: "323110155"
-}
-
-flag {
     name: "phy_to_native"
     namespace: "bluetooth"
     description: "Expose advertising PHY settings to native layer"
diff --git a/flags/hap.aconfig b/flags/hap.aconfig
index 15cfd9c..3ce320f 100644
--- a/flags/hap.aconfig
+++ b/flags/hap.aconfig
@@ -33,3 +33,13 @@
     description: "Allow user to control the preset of hearing aid devices"
     bug: "306236481"
 }
+
+flag {
+    name: "connect_hap_on_other_profile_connect"
+    namespace: "bluetooth"
+    description: "Try to connect HAP when other profile is getting connected"
+    bug: "379771539"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/sockets.aconfig b/flags/sockets.aconfig
index b27b6a5..5f00813 100644
--- a/flags/sockets.aconfig
+++ b/flags/sockets.aconfig
@@ -52,3 +52,13 @@
     bug: "374358112"
     is_exported: true
 }
+
+flag {
+    name: "avoid_l2c_processing_while_stack_shutdown"
+    namespace: "bluetooth"
+    description: "Avoid l2cap processing while stack is shutdown"
+    bug: "379731768"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/framework/Android.bp b/framework/Android.bp
index 0526650..e6a14f7 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -90,9 +90,6 @@
         "android.bluetooth",
         "com.android.bluetooth.jarjar",
     ],
-    plugins: [
-        "error_prone_android_framework",
-    ],
     aconfig_declarations: [
         "bluetooth_aconfig_flags",
     ],
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 10cb580..ba384ff 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -473,10 +473,12 @@
 
   public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioLocation(@NonNull android.bluetooth.BluetoothDevice);
+    method @FlaggedApi("com.android.bluetooth.flags.leaudio_broadcast_api_manage_primary_group") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getBroadcastToUnicastFallbackGroup();
     method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothLeAudioCodecStatus getCodecStatus(int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInbandRingtoneEnabled(int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothLeAudio.Callback);
+    method @FlaggedApi("com.android.bluetooth.flags.leaudio_broadcast_api_manage_primary_group") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setBroadcastToUnicastFallbackGroup(int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setCodecConfigPreference(int, @NonNull android.bluetooth.BluetoothLeAudioCodecConfig, @NonNull android.bluetooth.BluetoothLeAudioCodecConfig);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setVolume(@IntRange(from=0, to=255) int);
@@ -1304,8 +1306,9 @@
   }
 
   public final class DistanceMeasurementManager {
-    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getChannelSoundingMaxSupportedSecurityLevel(@NonNull android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getLocalChannelSoundingMaxSupportedSecurityLevel();
+    method @Deprecated @FlaggedApi("com.android.bluetooth.flags.channel_sounding_25q2_apis") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getChannelSoundingMaxSupportedSecurityLevel(@NonNull android.bluetooth.BluetoothDevice);
+    method @FlaggedApi("com.android.bluetooth.flags.channel_sounding_25q2_apis") @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.Set<java.lang.Integer> getChannelSoundingSupportedSecurityLevels();
+    method @Deprecated @FlaggedApi("com.android.bluetooth.flags.channel_sounding_25q2_apis") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getLocalChannelSoundingMaxSupportedSecurityLevel();
     method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.le.DistanceMeasurementMethod> getSupportedMethods();
     method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.os.CancellationSignal startMeasurementSession(@NonNull android.bluetooth.le.DistanceMeasurementParams, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.le.DistanceMeasurementSession.Callback);
   }
diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java
index adecd2d..30d17d7 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudio.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudio.java
@@ -1430,4 +1430,77 @@
             }
         }
     }
+
+    /**
+     * Sets broadcast to unicast fallback group.
+     *
+     * <p>In broadcast handover situations where unicast is unavailable, this group acts as the
+     * fallback.
+     *
+     * <p>A handover can occur when ongoing broadcast is interrupted with unicast streaming request.
+     *
+     * <p>On fallback group changed, {@link Callback#onBroadcastToUnicastFallbackGroupChanged} will
+     * be invoked.
+     *
+     * @param groupId the ID of the group to switch to if unicast fails during a broadcast handover,
+     *     {@link #GROUP_ID_INVALID} when there should be no such fallback group.
+     * @see BluetoothLeAudio#getGroupId()
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP)
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+    public void setBroadcastToUnicastFallbackGroup(int groupId) {
+        if (DBG) Log.d(TAG, "setBroadcastToUnicastFallbackGroup(" + groupId + ")");
+
+        final IBluetoothLeAudio service = getService();
+
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        } else if (mAdapter.isEnabled()) {
+            try {
+                service.setBroadcastToUnicastFallbackGroup(groupId, mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+            }
+        }
+    }
+
+    /**
+     * Gets broadcast to unicast fallback group.
+     *
+     * <p>In broadcast handover situations where unicast is unavailable, this group acts as the
+     * fallback.
+     *
+     * <p>A broadcast handover can occur when a {@link BluetoothLeBroadcast#startBroadcast} call is
+     * successful and there's an active unicast group.
+     *
+     * @return groupId the ID of the fallback group, {@link #GROUP_ID_INVALID} when adapter is
+     *     disabled
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP)
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+    public int getBroadcastToUnicastFallbackGroup() {
+        if (DBG) Log.d(TAG, "getBroadcastToUnicastFallbackGroup()");
+
+        final IBluetoothLeAudio service = getService();
+
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        } else if (mAdapter.isEnabled()) {
+            try {
+                return service.getBroadcastToUnicastFallbackGroup(mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+            }
+        }
+
+        return GROUP_ID_INVALID;
+    }
 }
diff --git a/framework/java/android/bluetooth/le/ChannelSoundingParams.java b/framework/java/android/bluetooth/le/ChannelSoundingParams.java
index ab5b8b4..b6c9529 100644
--- a/framework/java/android/bluetooth/le/ChannelSoundingParams.java
+++ b/framework/java/android/bluetooth/le/ChannelSoundingParams.java
@@ -22,8 +22,10 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * The {@link ChannelSoundingParams} provide a way to adjust distance measurement preferences for
@@ -51,6 +53,7 @@
     @interface LocationType {}
 
     /** @hide */
+    @Target(ElementType.TYPE_USE)
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(
             value = {
diff --git a/framework/java/android/bluetooth/le/DistanceMeasurementManager.java b/framework/java/android/bluetooth/le/DistanceMeasurementManager.java
index 7a5eb37..c5f07d2 100644
--- a/framework/java/android/bluetooth/le/DistanceMeasurementManager.java
+++ b/framework/java/android/bluetooth/le/DistanceMeasurementManager.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -35,12 +36,18 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.bluetooth.flags.Flags;
+
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
 
 /**
  * This class provides methods to perform distance measurement related operations. An application
@@ -164,8 +171,11 @@
      * @param remoteDevice remote device of channel sounding
      * @return max supported security level, {@link ChannelSoundingParams#CS_SECURITY_LEVEL_UNKNOWN}
      *     when Channel Sounding is not supported or encounters an internal error.
+     * @deprecated do not use it, this is meaningless, no alternative API.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_CHANNEL_SOUNDING_25Q2_APIS)
+    @Deprecated
     @SystemApi
     @RequiresBluetoothConnectPermission
     @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
@@ -194,8 +204,11 @@
      *
      * @return max supported security level, {@link ChannelSoundingParams#CS_SECURITY_LEVEL_UNKNOWN}
      *     when Channel Sounding is not supported or encounters an internal error.
+     * @deprecated use {@link #getChannelSoundingSupportedSecurityLevels} instead.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_CHANNEL_SOUNDING_25Q2_APIS)
+    @Deprecated
     @SystemApi
     @RequiresBluetoothConnectPermission
     @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
@@ -214,6 +227,35 @@
         return defaultValue;
     }
 
+    /**
+     * Get the set of supported security levels of channel sounding.
+     *
+     * <p>See: https://bluetooth.com/specifications/specs/core60-html/
+     *
+     * @return the set of supported security levels, empty when Channel Sounding is not supported or
+     *     encounters an internal error.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CHANNEL_SOUNDING_25Q2_APIS)
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+    public @NonNull Set<@CsSecurityLevel Integer> getChannelSoundingSupportedSecurityLevels() {
+        try {
+            IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
+            if (gatt == null) {
+                Log.e(TAG, "Bluetooth GATT is null");
+                return Collections.emptySet();
+            }
+            return Arrays.stream(gatt.getChannelSoundingSupportedSecurityLevels(mAttributionSource))
+                    .boxed()
+                    .collect(Collectors.toUnmodifiableSet());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get supported security Level - ", e);
+        }
+        return Collections.emptySet();
+    }
+
     @SuppressLint("AndroidFrameworkBluetoothPermission")
     private final IDistanceMeasurementCallback mCallbackWrapper =
             new IDistanceMeasurementCallback.Stub() {
diff --git a/service/Android.bp b/service/Android.bp
index 0a8e169..ccb7f7d 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -18,7 +18,7 @@
 
 java_defaults {
     name: "service-bluetooth-buildflags",
-    defaults: ["bluetooth_errorprone_rules"],
+    defaults: ["bluetooth_framework_errorprone_rules"],
 
     lint: {
         error_checks: [
diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java
index 5e9cca4..a78d857 100644
--- a/service/src/com/android/server/bluetooth/BluetoothManagerService.java
+++ b/service/src/com/android/server/bluetooth/BluetoothManagerService.java
@@ -253,13 +253,12 @@
         }
         mName = name;
         Log.v(TAG, "storeName(" + mName + "): Success");
-        mContext.sendBroadcastAsUser(
+        Intent intent =
                 new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)
                         .putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, name)
-                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
-                UserHandle.ALL,
-                BLUETOOTH_CONNECT,
-                getTempAllowlistBroadcastOptions());
+                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mContext.sendBroadcastAsUser(
+                intent, UserHandle.ALL, BLUETOOTH_CONNECT, getTempAllowlistBroadcastOptions());
     }
 
     private void storeAddress(String address) {
@@ -2216,13 +2215,12 @@
         if (mEnable) {
             long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
             String onDurationString =
-                    String.format(
-                            Locale.US,
+                    android.bluetooth.BluetoothUtils.formatSimple(
                             "%02d:%02d:%02d.%03d",
-                            (int) (onDuration / (1000 * 60 * 60)),
-                            (int) ((onDuration / (1000 * 60)) % 60),
-                            (int) ((onDuration / 1000) % 60),
-                            (int) (onDuration % 1000));
+                            onDuration / (1000 * 60 * 60),
+                            (onDuration / (1000 * 60)) % 60,
+                            (onDuration / 1000) % 60,
+                            onDuration % 1000);
             writer.println("  time since enabled: " + onDurationString);
         }
 
diff --git a/service/src/com/android/server/bluetooth/BluetoothServiceBinder.java b/service/src/com/android/server/bluetooth/BluetoothServiceBinder.java
index 95b4b0c..bb0e951 100644
--- a/service/src/com/android/server/bluetooth/BluetoothServiceBinder.java
+++ b/service/src/com/android/server/bluetooth/BluetoothServiceBinder.java
@@ -16,8 +16,6 @@
 
 package com.android.server.bluetooth;
 
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -30,7 +28,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
 import android.app.AppOpsManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetooth;
@@ -210,7 +207,6 @@
     }
 
     @Override
-    @RequiresPermission(BLUETOOTH_CONNECT)
     public String getName(AttributionSource source) {
         requireNonNull(source, "AttributionSource cannot be null in getName");
 
@@ -229,7 +225,6 @@
     }
 
     @Override
-    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
     public boolean onFactoryReset(AttributionSource source) {
         requireNonNull(source, "AttributionSource cannot be null in onFactoryReset");
 
@@ -249,7 +244,6 @@
     }
 
     @Override
-    @RequiresPermission(BLUETOOTH_CONNECT)
     public boolean enableBle(AttributionSource source, IBinder token) {
         requireNonNull(source, "AttributionSource cannot be null in enableBle");
         requireNonNull(token, "IBinder cannot be null in enableBle");
@@ -273,7 +267,6 @@
     }
 
     @Override
-    @RequiresPermission(BLUETOOTH_CONNECT)
     public boolean disableBle(AttributionSource source, IBinder token) {
         requireNonNull(source, "AttributionSource cannot be null in disableBle");
         requireNonNull(token, "IBinder cannot be null in disableBle");
@@ -302,7 +295,6 @@
     }
 
     @Override
-    @RequiresPermission(BLUETOOTH_PRIVILEGED)
     public int setBtHciSnoopLogMode(int mode) {
         BtPermissionUtils.enforcePrivileged(mContext);
 
@@ -310,7 +302,6 @@
     }
 
     @Override
-    @RequiresPermission(BLUETOOTH_PRIVILEGED)
     public int getBtHciSnoopLogMode() {
         BtPermissionUtils.enforcePrivileged(mContext);
 
@@ -333,21 +324,18 @@
     }
 
     @Override
-    @RequiresPermission(BLUETOOTH_PRIVILEGED)
     public boolean isAutoOnSupported() {
         BtPermissionUtils.enforcePrivileged(mContext);
         return mBluetoothManagerService.isAutoOnSupported();
     }
 
     @Override
-    @RequiresPermission(BLUETOOTH_PRIVILEGED)
     public boolean isAutoOnEnabled() {
         BtPermissionUtils.enforcePrivileged(mContext);
         return mBluetoothManagerService.isAutoOnEnabled();
     }
 
     @Override
-    @RequiresPermission(BLUETOOTH_PRIVILEGED)
     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
     public void setAutoOnEnabled(boolean status) {
         BtPermissionUtils.enforcePrivileged(mContext);
@@ -355,7 +343,6 @@
     }
 
     @Override
-    @RequiresPermission(DUMP)
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (mContext.checkCallingOrSelfPermission(DUMP) != PERMISSION_GRANTED) {
             // TODO(b/280890575): Throws SecurityException instead
diff --git a/service/src/com/android/server/bluetooth/BluetoothShellCommand.java b/service/src/com/android/server/bluetooth/BluetoothShellCommand.java
index 1c258ef..08acddb 100644
--- a/service/src/com/android/server/bluetooth/BluetoothShellCommand.java
+++ b/service/src/com/android/server/bluetooth/BluetoothShellCommand.java
@@ -18,6 +18,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.SuppressLint;
 import android.bluetooth.BluetoothAdapter;
 import android.content.AttributionSource;
 import android.os.Binder;
@@ -29,6 +30,7 @@
 
 import java.io.PrintWriter;
 
+@SuppressLint("AndroidFrameworkRequiresPermission")
 class BluetoothShellCommand extends BasicShellCommandHandler {
     private static final String TAG = BluetoothShellCommand.class.getSimpleName();
 
diff --git a/service/src/com/android/server/bluetooth/BtPermissionUtils.java b/service/src/com/android/server/bluetooth/BtPermissionUtils.java
index c9ee6b3..b9e736b 100644
--- a/service/src/com/android/server/bluetooth/BtPermissionUtils.java
+++ b/service/src/com/android/server/bluetooth/BtPermissionUtils.java
@@ -77,7 +77,7 @@
      *
      * <p>Should be used in situations where the app op should not be noted.
      */
-    @SuppressLint("AndroidFrameworkRequiresPermission")
+    @SuppressLint("AndroidFrameworkRequiresPermission") // This method enforces the permission
     @RequiresPermission(BLUETOOTH_CONNECT)
     static boolean checkConnectPermissionForDataDelivery(
             Context ctx,
@@ -109,6 +109,7 @@
      *
      * <p>Return the error description if this caller is not allowed to toggle Bluetooth
      */
+    @RequiresPermission(BLUETOOTH_CONNECT)
     String callerCanToggle(
             Context ctx,
             AttributionSource source,
@@ -168,6 +169,7 @@
         return callingAppId == mSystemUiUid;
     }
 
+    @SuppressLint("AndroidFrameworkRequiresPermission") // Permission is not enforced, only checked
     private static boolean isPrivileged(Context ctx, int pid, int uid) {
         return (ctx.checkPermission(BLUETOOTH_PRIVILEGED, pid, uid) == PERMISSION_GRANTED)
                 || (ctx.getPackageManager().checkSignatures(uid, SYSTEM_UID) == SIGNATURE_MATCH);
@@ -209,7 +211,7 @@
         }
         UserHandle deviceOwnerUser = null;
         ComponentName deviceOwnerComponent = null;
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             deviceOwnerUser = devicePolicyManager.getDeviceOwnerUser();
             deviceOwnerComponent = devicePolicyManager.getDeviceOwnerComponentOnAnyUser();
@@ -227,7 +229,7 @@
     }
 
     private static boolean isSystem(Context ctx, String packageName, int uid) {
-        long ident = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
             ApplicationInfo info =
                     ctx.getPackageManager()
diff --git a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
index 8c4a2c9..2cd702f 100644
--- a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
+++ b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
@@ -46,6 +46,7 @@
 import static org.mockito.Mockito.validateMockitoUsage;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.SuppressLint;
 import android.app.PropertyInvalidatedCache;
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothCallback;
@@ -87,6 +88,7 @@
 import java.util.stream.IntStream;
 
 @RunWith(ParameterizedAndroidJunit4.class)
+@SuppressLint("AndroidFrameworkRequiresPermission")
 public class BluetoothManagerServiceTest {
 
     @Rule public final SetFlagsRule mSetFlagsRule;
diff --git a/service/tests/src/com/android/server/bluetooth/BluetoothServiceBinderTest.java b/service/tests/src/com/android/server/bluetooth/BluetoothServiceBinderTest.java
index 071c429..40e6f4b 100644
--- a/service/tests/src/com/android/server/bluetooth/BluetoothServiceBinderTest.java
+++ b/service/tests/src/com/android/server/bluetooth/BluetoothServiceBinderTest.java
@@ -38,6 +38,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.quality.Strictness.STRICT_STUBS;
 
+import android.annotation.SuppressLint;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyManager;
 import android.bluetooth.IBluetoothManagerCallback;
@@ -77,6 +78,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@SuppressLint("AndroidFrameworkRequiresPermission")
 public class BluetoothServiceBinderTest {
     private static final String TAG = BluetoothServiceBinderTest.class.getSimpleName();
     private static final String LOG_COMPAT_CHANGE = "android.permission.LOG_COMPAT_CHANGE";
diff --git a/service/tests/src/com/android/server/bluetooth/BluetoothShellCommandTest.java b/service/tests/src/com/android/server/bluetooth/BluetoothShellCommandTest.java
index 7f1df5b..d2b290b 100644
--- a/service/tests/src/com/android/server/bluetooth/BluetoothShellCommandTest.java
+++ b/service/tests/src/com/android/server/bluetooth/BluetoothShellCommandTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.SuppressLint;
 import android.bluetooth.BluetoothAdapter;
 import android.os.Binder;
 import android.os.RemoteException;
@@ -53,6 +54,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@SuppressLint("AndroidFrameworkRequiresPermission")
 public class BluetoothShellCommandTest {
     @Rule public final Expect expect = Expect.create();
 
diff --git a/system/audio_hal_interface/a2dp_encoding.h b/system/audio_hal_interface/a2dp_encoding.h
index 5c8e68c..d7812bb 100644
--- a/system/audio_hal_interface/a2dp_encoding.h
+++ b/system/audio_hal_interface/a2dp_encoding.h
@@ -198,8 +198,8 @@
 }  // namespace audio
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<::bluetooth::audio::a2dp::BluetoothAudioStatus>
     : enum_formatter<::bluetooth::audio::a2dp::BluetoothAudioStatus> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/audio_hal_interface/a2dp_encoding_host.cc b/system/audio_hal_interface/a2dp_encoding_host.cc
index 65c5eba..2436aa0 100644
--- a/system/audio_hal_interface/a2dp_encoding_host.cc
+++ b/system/audio_hal_interface/a2dp_encoding_host.cc
@@ -48,12 +48,12 @@
   A2DP_CTRL_GET_PRESENTATION_POSITION,
 } tA2DP_CTRL_CMD;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tUIPC_EVENT> : enum_formatter<tUIPC_EVENT> {};
 template <>
 struct formatter<tA2DP_CTRL_CMD> : enum_formatter<tA2DP_CTRL_CMD> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace {
 
diff --git a/system/audio_hal_interface/aidl/a2dp/a2dp_encoding_aidl.cc b/system/audio_hal_interface/aidl/a2dp/a2dp_encoding_aidl.cc
index cde3eed..8f4d075 100644
--- a/system/audio_hal_interface/aidl/a2dp/a2dp_encoding_aidl.cc
+++ b/system/audio_hal_interface/aidl/a2dp/a2dp_encoding_aidl.cc
@@ -40,14 +40,14 @@
   A2DP_CTRL_GET_PRESENTATION_POSITION,
 } tA2DP_CTRL_CMD;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tA2DP_CTRL_CMD> : enum_formatter<tA2DP_CTRL_CMD> {};
 template <>
 struct formatter<audio_usage_t> : enum_formatter<audio_usage_t> {};
 template <>
 struct formatter<audio_content_type_t> : enum_formatter<audio_content_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace bluetooth {
 namespace audio {
diff --git a/system/audio_hal_interface/aidl/a2dp/client_interface_aidl.cc b/system/audio_hal_interface/aidl/a2dp/client_interface_aidl.cc
index 42af94d..6303bbd 100644
--- a/system/audio_hal_interface/aidl/a2dp/client_interface_aidl.cc
+++ b/system/audio_hal_interface/aidl/a2dp/client_interface_aidl.cc
@@ -215,7 +215,7 @@
   }
 
   log::info("IBluetoothAudioProvidersFactory::openProvider() returned {}{}",
-            fmt::ptr(provider_.get()), (provider_->isRemote() ? " (remote)" : " (local)"));
+            std::format_ptr(provider_.get()), (provider_->isRemote() ? " (remote)" : " (local)"));
 }
 
 void BluetoothAudioClientInterface::binderDiedCallbackAidl(void* ptr) {
diff --git a/system/audio_hal_interface/aidl/audio_ctrl_ack.h b/system/audio_hal_interface/aidl/audio_ctrl_ack.h
index 5cd051c..336bb1f 100644
--- a/system/audio_hal_interface/aidl/audio_ctrl_ack.h
+++ b/system/audio_hal_interface/aidl/audio_ctrl_ack.h
@@ -61,7 +61,7 @@
 }  // namespace audio
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::audio::aidl::BluetoothAudioCtrlAck> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/audio_hal_interface/aidl/client_interface_aidl.cc b/system/audio_hal_interface/aidl/client_interface_aidl.cc
index e8b143f..176b355 100644
--- a/system/audio_hal_interface/aidl/client_interface_aidl.cc
+++ b/system/audio_hal_interface/aidl/client_interface_aidl.cc
@@ -182,7 +182,7 @@
   }
 
   log::info("IBluetoothAudioProvidersFactory::openProvider() returned {}{}",
-            fmt::ptr(provider_.get()), (provider_->isRemote() ? " (remote)" : " (local)"));
+            std::format_ptr(provider_.get()), (provider_->isRemote() ? " (remote)" : " (local)"));
 }
 
 BluetoothAudioSinkClientInterface::BluetoothAudioSinkClientInterface(
diff --git a/system/audio_hal_interface/aidl/hearing_aid_software_encoding_aidl.cc b/system/audio_hal_interface/aidl/hearing_aid_software_encoding_aidl.cc
index b6a7618..0cf9d82 100644
--- a/system/audio_hal_interface/aidl/hearing_aid_software_encoding_aidl.cc
+++ b/system/audio_hal_interface/aidl/hearing_aid_software_encoding_aidl.cc
@@ -24,12 +24,12 @@
 #include "client_interface_aidl.h"
 #include "osi/include/properties.h"
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<audio_usage_t> : enum_formatter<audio_usage_t> {};
 template <>
 struct formatter<audio_content_type_t> : enum_formatter<audio_content_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace {
 
diff --git a/system/audio_hal_interface/aidl/hfp_client_interface_aidl.h b/system/audio_hal_interface/aidl/hfp_client_interface_aidl.h
index 5dbf8ad..6b75870 100644
--- a/system/audio_hal_interface/aidl/hfp_client_interface_aidl.h
+++ b/system/audio_hal_interface/aidl/hfp_client_interface_aidl.h
@@ -170,8 +170,8 @@
 }  // namespace audio
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::audio::aidl::hfp::tHFP_CTRL_CMD>
     : enum_formatter<bluetooth::audio::aidl::hfp::tHFP_CTRL_CMD> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/audio_hal_interface/hal_version_manager.cc b/system/audio_hal_interface/hal_version_manager.cc
index 2068b76..f9843df 100644
--- a/system/audio_hal_interface/hal_version_manager.cc
+++ b/system/audio_hal_interface/hal_version_manager.cc
@@ -112,7 +112,7 @@
                    "V2_1::IBluetoothAudioProvidersFactory::getService() failed");
 
   log::info("V2_1::IBluetoothAudioProvidersFactory::getService() returned {}{}",
-            fmt::ptr(providers_factory.get()),
+            std::format_ptr(providers_factory.get()),
             (providers_factory->isRemote() ? " (remote)" : " (local)"));
   return providers_factory;
 }
@@ -129,7 +129,7 @@
                    "V2_0::IBluetoothAudioProvidersFactory::getService() failed");
 
   log::info("V2_0::IBluetoothAudioProvidersFactory::getService() returned {}{}",
-            fmt::ptr(providers_factory.get()),
+            std::format_ptr(providers_factory.get()),
             (providers_factory->isRemote() ? " (remote)" : " (local)"));
   guard.unlock();
   return providers_factory;
diff --git a/system/audio_hal_interface/hidl/a2dp_encoding_hidl.cc b/system/audio_hal_interface/hidl/a2dp_encoding_hidl.cc
index 2926e73..62d5178 100644
--- a/system/audio_hal_interface/hidl/a2dp_encoding_hidl.cc
+++ b/system/audio_hal_interface/hidl/a2dp_encoding_hidl.cc
@@ -39,14 +39,14 @@
   A2DP_CTRL_GET_PRESENTATION_POSITION,
 } tA2DP_CTRL_CMD;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tA2DP_CTRL_CMD> : enum_formatter<tA2DP_CTRL_CMD> {};
 template <>
 struct formatter<audio_usage_t> : enum_formatter<audio_usage_t> {};
 template <>
 struct formatter<audio_content_type_t> : enum_formatter<audio_content_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace bluetooth {
 namespace audio {
diff --git a/system/audio_hal_interface/hidl/client_interface_hidl.cc b/system/audio_hal_interface/hidl/client_interface_hidl.cc
index c698727..4bea626 100644
--- a/system/audio_hal_interface/hidl/client_interface_hidl.cc
+++ b/system/audio_hal_interface/hidl/client_interface_hidl.cc
@@ -314,7 +314,7 @@
   }
 
   log::info("IBluetoothAudioProvidersFactory::openProvider() returned {}{}",
-            fmt::ptr(provider_.get()), (provider_->isRemote() ? " (remote)" : " (local)"));
+            std::format_ptr(provider_.get()), (provider_->isRemote() ? " (remote)" : " (local)"));
 }
 
 void BluetoothAudioClientInterface::FetchAudioProvider_2_1() {
@@ -376,7 +376,8 @@
   }
 
   log::info("IBluetoothAudioProvidersFactory::openProvider() returned {}{}",
-            fmt::ptr(provider_2_1_.get()), (provider_2_1_->isRemote() ? " (remote)" : " (local)"));
+            std::format_ptr(provider_2_1_.get()),
+            (provider_2_1_->isRemote() ? " (remote)" : " (local)"));
 }
 
 BluetoothAudioSinkClientInterface::BluetoothAudioSinkClientInterface(
diff --git a/system/audio_hal_interface/hidl/client_interface_hidl.h b/system/audio_hal_interface/hidl/client_interface_hidl.h
index 09a9165..39347e7 100644
--- a/system/audio_hal_interface/hidl/client_interface_hidl.h
+++ b/system/audio_hal_interface/hidl/client_interface_hidl.h
@@ -266,8 +266,8 @@
 }  // namespace audio
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::audio::hidl::BluetoothAudioCtrlAck>
     : enum_formatter<bluetooth::audio::hidl::BluetoothAudioCtrlAck> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/audio_hal_interface/hidl/hearing_aid_software_encoding_hidl.cc b/system/audio_hal_interface/hidl/hearing_aid_software_encoding_hidl.cc
index f80aac6..b745914 100644
--- a/system/audio_hal_interface/hidl/hearing_aid_software_encoding_hidl.cc
+++ b/system/audio_hal_interface/hidl/hearing_aid_software_encoding_hidl.cc
@@ -24,12 +24,12 @@
 #include "client_interface_hidl.h"
 #include "osi/include/properties.h"
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<audio_usage_t> : enum_formatter<audio_usage_t> {};
 template <>
 struct formatter<audio_content_type_t> : enum_formatter<audio_content_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace {
 
diff --git a/system/bta/ag/bta_ag_cmd.cc b/system/bta/ag/bta_ag_cmd.cc
index f5a09ec..af1df17 100644
--- a/system/bta/ag/bta_ag_cmd.cc
+++ b/system/bta/ag/bta_ag_cmd.cc
@@ -225,7 +225,7 @@
   *p++ = '\n';
 
   /* copy result code string */
-  strlcpy(p, result->result_string, sizeof(buf) - 2);
+  osi_strlcpy(p, result->result_string, sizeof(buf) - 2);
 
   if (p_scb->conn_service == BTA_AG_HSP) {
     /* If HSP then ":"symbol should be changed as "=" for HSP compatibility */
@@ -541,7 +541,7 @@
     /* Add EOF */
     trim_data[j] = '\0';
     str_leng = str_leng - 4;
-    strlcpy(unat_result, trim_data, str_leng + 1);
+    osi_strlcpy(unat_result, trim_data, str_leng + 1);
     j = 0;
 
     if (str_leng < 4) {
@@ -621,7 +621,7 @@
     bta_ag_send_error(p_scb, BTA_AG_ERR_TEXT_TOO_LONG);
     return;
   }
-  strlcpy(val.str, p_arg, sizeof(val.str));
+  osi_strlcpy(val.str, p_arg, sizeof(val.str));
 
   /* call callback with event */
   if (command_id & 0xff00) {
@@ -899,7 +899,7 @@
     bta_ag_send_error(p_scb, BTA_AG_ERR_TEXT_TOO_LONG);
     return;
   }
-  strlcpy(val.str, p_arg, sizeof(val.str));
+  osi_strlcpy(val.str, p_arg, sizeof(val.str));
 
   /**
    * Unless this this is a local event, by default we'll forward
@@ -1397,7 +1397,7 @@
     val.hdr.app_id = p_scb->app_id;
     val.hdr.status = BTA_AG_SUCCESS;
     val.num = 0;
-    strlcpy(val.str, p_arg, sizeof(val.str));
+    osi_strlcpy(val.str, p_arg, sizeof(val.str));
     (*bta_ag_cb.p_cback)(BTA_AG_AT_UNAT_EVT, (tBTA_AG*)&val);
   } else {
     bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
diff --git a/system/bta/ag/bta_ag_int.h b/system/bta/ag/bta_ag_int.h
index 21d3936..ce9ff62 100644
--- a/system/bta/ag/bta_ag_int.h
+++ b/system/bta/ag/bta_ag_int.h
@@ -476,11 +476,11 @@
  */
 void bta_ag_stream_suspended();
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_AG_SCO_APTX_SWB_SETTINGS> : enum_formatter<tBTA_AG_SCO_APTX_SWB_SETTINGS> {};
 template <>
 struct formatter<tBTA_AG_SCO> : enum_formatter<tBTA_AG_SCO> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTA_AG_INT_H */
diff --git a/system/bta/ag/bta_ag_main.cc b/system/bta/ag/bta_ag_main.cc
index a1143a5..2b95b5d 100644
--- a/system/bta/ag/bta_ag_main.cc
+++ b/system/bta/ag/bta_ag_main.cc
@@ -491,7 +491,7 @@
     data.api_register.app_id = app_id;
     for (int i = 0; i < BTA_AG_NUM_IDX; i++) {
       if (!service_names[i].empty()) {
-        strlcpy(data.api_register.p_name[i], service_names[i].c_str(), BTA_SERVICE_NAME_LEN);
+        osi_strlcpy(data.api_register.p_name[i], service_names[i].c_str(), BTA_SERVICE_NAME_LEN);
       } else {
         data.api_register.p_name[i][0] = 0;
       }
diff --git a/system/bta/ag/bta_ag_rfc.cc b/system/bta/ag/bta_ag_rfc.cc
index 46b26b1..1943492 100644
--- a/system/bta/ag/bta_ag_rfc.cc
+++ b/system/bta/ag/bta_ag_rfc.cc
@@ -107,7 +107,7 @@
 static void bta_ag_mgmt_cback(const tPORT_RESULT code, uint16_t port_handle, uint16_t handle) {
   tBTA_AG_SCB* p_scb = bta_ag_scb_by_idx(handle);
   log::verbose("code={}, port_handle={}, scb_handle={}, p_scb=0x{}", code, port_handle, handle,
-               fmt::ptr(p_scb));
+               std::format_ptr(p_scb));
   if (p_scb == nullptr) {
     log::warn("cannot find scb, code={}, port_handle={}, handle={}", code, port_handle, handle);
     return;
@@ -262,10 +262,10 @@
         log::error(
                 "RFCOMM_CreateConnectionWithSecurity ERROR {}, p_scb={}, "
                 "services=0x{:x}, mgmt_cback_index={}",
-                status, fmt::ptr(p_scb), services, management_callback_index);
+                status, std::format_ptr(p_scb), services, management_callback_index);
       }
-      log::verbose("p_scb=0x{}, services=0x{:04x}, mgmt_cback_index={}", fmt::ptr(p_scb), services,
-                   management_callback_index);
+      log::verbose("p_scb=0x{}, services=0x{:04x}, mgmt_cback_index={}", std::format_ptr(p_scb),
+                   services, management_callback_index);
     }
   }
 }
@@ -332,7 +332,7 @@
           bta_ag_uuid[p_scb->conn_service], p_scb->peer_scn, false, BTA_AG_MTU, p_scb->peer_addr,
           &(p_scb->conn_handle), bta_ag_mgmt_cback_tbl[management_callback_index],
           BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT);
-  log::verbose("p_scb=0x{}, conn_handle={}, mgmt_cback_index={}, status={}", fmt::ptr(p_scb),
+  log::verbose("p_scb=0x{}, conn_handle={}, mgmt_cback_index={}, status={}", std::format_ptr(p_scb),
                p_scb->conn_handle, management_callback_index, status);
   if (status == PORT_SUCCESS) {
     bta_ag_setup_port(p_scb, p_scb->conn_handle);
diff --git a/system/bta/aics/include/aics/api.h b/system/bta/aics/include/aics/api.h
index b81132e..7eb76e5 100644
--- a/system/bta/aics/include/aics/api.h
+++ b/system/bta/aics/include/aics/api.h
@@ -51,9 +51,9 @@
 GainMode parseGainModeField(uint8_t data);
 }  // namespace bluetooth::aics
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::aics::Mute> : enum_formatter<bluetooth::aics::Mute> {};
 template <>
 struct formatter<bluetooth::aics::GainMode> : enum_formatter<bluetooth::aics::GainMode> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/av/bta_av_aact.cc b/system/bta/av/bta_av_aact.cc
index 0099c33..0378be7 100644
--- a/system/bta/av/bta_av_aact.cc
+++ b/system/bta/av/bta_av_aact.cc
@@ -367,7 +367,7 @@
   uint16_t sec_len = 0;
 
   log::verbose("peer_address: {} avdt_handle: {} event=0x{:x} scb_index={} p_scb={}", bd_addr,
-               handle, event, scb_index, fmt::ptr(p_scb));
+               handle, event, scb_index, std::format_ptr(p_scb));
 
   if (p_data) {
     if (event == AVDT_SECURITY_IND_EVT) {
@@ -1926,7 +1926,7 @@
   BT_HDR* p_buf;
 
   log::info("peer {} bta_handle:0x{:x} audio_open_cnt:{}, p_data {} start:{}", p_scb->PeerAddress(),
-            p_scb->hndl, bta_av_cb.audio_open_cnt, fmt::ptr(p_data), start);
+            p_scb->hndl, bta_av_cb.audio_open_cnt, std::format_ptr(p_data), start);
 
   bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->PeerAddress());
   BTM_unblock_role_switch_and_sniff_mode_for(p_scb->PeerAddress());
diff --git a/system/bta/av/bta_av_api.cc b/system/bta/av/bta_av_api.cc
index 8a47678..0f2fcbd 100644
--- a/system/bta/av/bta_av_api.cc
+++ b/system/bta/av/bta_av_api.cc
@@ -119,7 +119,7 @@
   p_buf->hdr.layer_specific = chnl;
   p_buf->hdr.event = BTA_AV_API_REGISTER_EVT;
   if (p_service_name) {
-    strlcpy(p_buf->p_service_name, p_service_name, BTA_SERVICE_NAME_LEN);
+    osi_strlcpy(p_buf->p_service_name, p_service_name, BTA_SERVICE_NAME_LEN);
   } else {
     p_buf->p_service_name[0] = 0;
   }
diff --git a/system/bta/av/bta_av_int.h b/system/bta/av/bta_av_int.h
index 7d98cf2..c9c176c 100644
--- a/system/bta/av/bta_av_int.h
+++ b/system/bta/av/bta_av_int.h
@@ -807,9 +807,9 @@
 void bta_av_st_rc_timer(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data);
 void bta_av_api_set_peer_sep(tBTA_AV_DATA* p_data);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_AV_RS_RES> : enum_formatter<tBTA_AV_RS_RES> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTA_AV_INT_H */
diff --git a/system/bta/av/bta_av_ssm.cc b/system/bta/av/bta_av_ssm.cc
index 84d9561..cbf83f5 100644
--- a/system/bta/av/bta_av_ssm.cc
+++ b/system/bta/av/bta_av_ssm.cc
@@ -440,13 +440,13 @@
 
   if (previous_state != p_scb->state) {
     log::info("peer {} p_scb={:#x}({}) AV event=0x{:x}({}) state={}({}) -> {}({})",
-              p_scb->PeerAddress(), p_scb->hndl, fmt::ptr(p_scb), event, bta_av_evt_code(event),
-              previous_state, bta_av_sst_code(previous_state), p_scb->state,
+              p_scb->PeerAddress(), p_scb->hndl, std::format_ptr(p_scb), event,
+              bta_av_evt_code(event), previous_state, bta_av_sst_code(previous_state), p_scb->state,
               bta_av_sst_code(p_scb->state));
 
   } else {
     log::verbose("peer {} p_scb={:#x}({}) AV event=0x{:x}({}) state={}({})", p_scb->PeerAddress(),
-                 p_scb->hndl, fmt::ptr(p_scb), event, bta_av_evt_code(event), p_scb->state,
+                 p_scb->hndl, std::format_ptr(p_scb), event, bta_av_evt_code(event), p_scb->state,
                  bta_av_sst_code(p_scb->state));
   }
 
@@ -521,7 +521,7 @@
 
   log::verbose("peer {} AV (hndl=0x{:x}) state={}({}) next state={}({}) p_scb={}",
                p_scb->PeerAddress(), p_scb->hndl, p_scb->state, bta_av_sst_code(p_scb->state),
-               next_state, bta_av_sst_code(next_state), fmt::ptr(p_scb));
+               next_state, bta_av_sst_code(next_state), std::format_ptr(p_scb));
 
   p_scb->state = next_state;
 }
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index d87dbb6..d38a232 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -19,6 +19,7 @@
 #include <base/functional/callback.h>
 #include <base/strings/string_number_conversions.h>
 #include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
 #include <hardware/bt_csis.h>
 #include <hardware/bt_gatt_types.h>
 #include <stdio.h>
@@ -1918,7 +1919,9 @@
 
     device->connecting_actively = false;
     device->conn_id = evt.conn_id;
-
+    if (com::android::bluetooth::flags::gatt_queue_cleanup_connected()) {
+      BtaGattQueue::Clean(evt.conn_id);
+    }
     /* Verify bond */
     if (BTM_SecIsSecurityPending(device->addr)) {
       /* if security collision happened, wait for encryption done
diff --git a/system/bta/dm/bta_dm_device_search.cc b/system/bta/dm/bta_dm_device_search.cc
index c80f763..7c2c30e 100644
--- a/system/bta/dm/bta_dm_device_search.cc
+++ b/system/bta/dm/bta_dm_device_search.cc
@@ -490,8 +490,9 @@
   bta_dm_search_cb.peer_bdaddr = remote_bd_addr;
 
   log::verbose("name_discover_done = {} p_btm_inq_info 0x{} state = {}, transport={}",
-               bta_dm_search_cb.name_discover_done, fmt::ptr(bta_dm_search_cb.p_btm_inq_info),
-               bta_dm_search_get_state(), transport);
+               bta_dm_search_cb.name_discover_done,
+               std::format_ptr(bta_dm_search_cb.p_btm_inq_info), bta_dm_search_get_state(),
+               transport);
 
   if (bta_dm_search_cb.p_btm_inq_info) {
     log::verbose("appl_knows_rem_name {}", bta_dm_search_cb.p_btm_inq_info->appl_knows_rem_name);
diff --git a/system/bta/dm/bta_dm_device_search_int.h b/system/bta/dm/bta_dm_device_search_int.h
index 68df101..28e3fda 100644
--- a/system/bta/dm/bta_dm_device_search_int.h
+++ b/system/bta/dm/bta_dm_device_search_int.h
@@ -90,9 +90,9 @@
   tBTA_DM_SEARCH_CBACK* p_csis_scan_cback;
 } tBTA_DM_SEARCH_CB;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_DM_DEV_SEARCH_EVT> : enum_formatter<tBTA_DM_DEV_SEARCH_EVT> {};
 template <>
 struct formatter<tBTA_DM_DEVICE_SEARCH_STATE> : enum_formatter<tBTA_DM_DEVICE_SEARCH_STATE> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/dm/bta_dm_disc_int.h b/system/bta/dm/bta_dm_disc_int.h
index df8d2f2..6c89f89 100644
--- a/system/bta/dm/bta_dm_disc_int.h
+++ b/system/bta/dm/bta_dm_disc_int.h
@@ -131,10 +131,10 @@
 void bta_dm_sdp_received_di(const RawAddress& bd_addr, tSDP_DI_GET_RECORD& di_record);
 #endif
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_DM_DISC_EVT> : enum_formatter<tBTA_DM_DISC_EVT> {};
 template <>
 struct formatter<tBTA_DM_SERVICE_DISCOVERY_STATE>
     : enum_formatter<tBTA_DM_SERVICE_DISCOVERY_STATE> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/dm/bta_dm_disc_sdp.cc b/system/bta/dm/bta_dm_disc_sdp.cc
index f4b1b13..588e7af 100644
--- a/system/bta/dm/bta_dm_disc_sdp.cc
+++ b/system/bta/dm/bta_dm_disc_sdp.cc
@@ -264,7 +264,7 @@
     // Copy the raw_data to the discovery result structure
     if (p_sdp_db != NULL && p_sdp_db->raw_used != 0 && p_sdp_db->raw_data != NULL) {
       log::verbose("raw_data used = 0x{:x} raw_data_ptr = 0x{}", p_sdp_db->raw_used,
-                   fmt::ptr(p_sdp_db->raw_data));
+                   std::format_ptr(p_sdp_db->raw_data));
 
       p_sdp_db->raw_data = NULL;  // no need to free this - it is a global assigned.
       p_sdp_db->raw_used = 0;
diff --git a/system/bta/dm/bta_dm_int.h b/system/bta/dm/bta_dm_int.h
index d775755..e79facd 100644
--- a/system/bta/dm/bta_dm_int.h
+++ b/system/bta/dm/bta_dm_int.h
@@ -365,9 +365,9 @@
                                 uint16_t subrate_max, uint16_t max_latency, uint16_t cont_num,
                                 uint16_t timeout);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_DM_CONN_STATE> : enum_formatter<tBTA_DM_CONN_STATE> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTA_DM_INT_H */
diff --git a/system/bta/gatt/bta_gattc_act.cc b/system/bta/gatt/bta_gattc_act.cc
index c1a8e95..37ecf8f 100644
--- a/system/bta/gatt/bta_gattc_act.cc
+++ b/system/bta/gatt/bta_gattc_act.cc
@@ -837,7 +837,8 @@
       bta_gattc_send_mtu_response(p_clcb, p_data, current_mtu);
       return;
     case MTU_EXCHANGE_IN_PROGRESS:
-      log::info("Enqueue MTU Request  - waiting for response on p_clcb {}", fmt::ptr(p_clcb));
+      log::info("Enqueue MTU Request  - waiting for response on p_clcb {}",
+                std::format_ptr(p_clcb));
       /* MTU request is in progress and this one will not be sent to remote
        * device. Just push back on the queue and response will be sent up to
        * the upper layer when MTU Exchange will be completed.
@@ -1358,7 +1359,7 @@
     auto outstanding_conn_ids = GATTC_GetAndRemoveListOfConnIdsWaitingForMtuRequest(p_clcb->bda);
     for (auto conn_id : outstanding_conn_ids) {
       tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
-      log::debug("Continue MTU request clcb {}", fmt::ptr(p_clcb));
+      log::debug("Continue MTU request clcb {}", std::format_ptr(p_clcb));
       if (p_clcb) {
         log::debug("Continue MTU request for client conn_id=0x{:04x}", conn_id);
         bta_gattc_continue(p_clcb);
diff --git a/system/bta/gatt/bta_gattc_int.h b/system/bta/gatt/bta_gattc_int.h
index 070e9c5..1bfd741 100644
--- a/system/bta/gatt/bta_gattc_int.h
+++ b/system/bta/gatt/bta_gattc_int.h
@@ -532,7 +532,7 @@
   }
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_GATTC_CB_STATE> : enum_formatter<tBTA_GATTC_CB_STATE> {};
 template <>
@@ -541,6 +541,6 @@
 struct formatter<tBTA_GATTC_STATE> : enum_formatter<tBTA_GATTC_STATE> {};
 template <>
 struct formatter<RobustCachingSupport> : enum_formatter<RobustCachingSupport> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTA_GATTC_INT_H */
diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc
index 70f528e..cf07f89 100644
--- a/system/bta/gatt/bta_gattc_utils.cc
+++ b/system/bta/gatt/bta_gattc_utils.cc
@@ -515,7 +515,7 @@
         /* Handled, free command below and continue with a p_q_cmd_queue */
         break;
       case MTU_EXCHANGE_IN_PROGRESS:
-        log::warn("Waiting p_clcb {}", fmt::ptr(p_clcb));
+        log::warn("Waiting p_clcb {}", std::format_ptr(p_clcb));
         return;
       case MTU_EXCHANGE_NOT_DONE_YET:
         p_clcb->p_q_cmd_queue.pop_front();
diff --git a/system/bta/has/has_client.cc b/system/bta/has/has_client.cc
index 1c74e1f..74873bd 100644
--- a/system/bta/has/has_client.cc
+++ b/system/bta/has/has_client.cc
@@ -2008,7 +2008,9 @@
     }
 
     device->conn_id = evt.conn_id;
-
+    if (com::android::bluetooth::flags::gatt_queue_cleanup_connected()) {
+      BtaGattQueue::Clean(evt.conn_id);
+    }
     if (BTM_SecIsSecurityPending(device->addr)) {
       /* if security collision happened, wait for encryption done
        * (BTA_GATTC_ENC_CMPL_CB_EVT)
diff --git a/system/bta/has/has_ctp.h b/system/bta/has/has_ctp.h
index 3de5b46..4b62426 100644
--- a/system/bta/has/has_ctp.h
+++ b/system/bta/has/has_ctp.h
@@ -257,9 +257,9 @@
 }  // namespace has
 }  // namespace bluetooth::le_audio
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::le_audio::has::HasCtpNtf> : ostream_formatter {};
 template <>
 struct formatter<bluetooth::le_audio::has::HasCtpOp> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/has/has_preset.h b/system/bta/has/has_preset.h
index 48c6185..60d8942 100644
--- a/system/bta/has/has_preset.h
+++ b/system/bta/has/has_preset.h
@@ -99,7 +99,7 @@
 }  // namespace has
 }  // namespace bluetooth::le_audio
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::le_audio::has::HasPreset> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/has/has_types.h b/system/bta/has/has_types.h
index 5041838..2a72098 100644
--- a/system/bta/has/has_types.h
+++ b/system/bta/has/has_types.h
@@ -402,7 +402,7 @@
 }  // namespace has
 }  // namespace bluetooth::le_audio
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::le_audio::has::HasDevice> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/hd/bta_hd_api.cc b/system/bta/hd/bta_hd_api.cc
index aa42481..336c24e 100644
--- a/system/bta/hd/bta_hd_api.cc
+++ b/system/bta/hd/bta_hd_api.cc
@@ -111,19 +111,19 @@
   p_buf->hdr.event = BTA_HD_API_REGISTER_APP_EVT;
 
   if (p_app_info->p_name) {
-    strlcpy(p_buf->name, p_app_info->p_name, BTA_HD_APP_NAME_LEN);
+    osi_strlcpy(p_buf->name, p_app_info->p_name, BTA_HD_APP_NAME_LEN);
   } else {
     p_buf->name[0] = '\0';
   }
 
   if (p_app_info->p_description) {
-    strlcpy(p_buf->description, p_app_info->p_description, BTA_HD_APP_DESCRIPTION_LEN);
+    osi_strlcpy(p_buf->description, p_app_info->p_description, BTA_HD_APP_DESCRIPTION_LEN);
   } else {
     p_buf->description[0] = '\0';
   }
 
   if (p_app_info->p_provider) {
-    strlcpy(p_buf->provider, p_app_info->p_provider, BTA_HD_APP_PROVIDER_LEN);
+    osi_strlcpy(p_buf->provider, p_app_info->p_provider, BTA_HD_APP_PROVIDER_LEN);
   } else {
     p_buf->provider[0] = '\0';
   }
diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc
index 9838ebd..e27b420 100644
--- a/system/bta/hearing_aid/hearing_aid.cc
+++ b/system/bta/hearing_aid/hearing_aid.cc
@@ -524,6 +524,10 @@
 
     hearingDevice->conn_id = conn_id;
 
+    if (com::android::bluetooth::flags::gatt_queue_cleanup_connected()) {
+      BtaGattQueue::Clean(conn_id);
+    }
+
     uint64_t hi_sync_id = hearingDevice->hi_sync_id;
 
     // If there a background connection to the other device of a pair, promote
@@ -1705,7 +1709,7 @@
       if (!strftime(temptime, sizeof(temptime), "%H:%M:%S", tstamp)) {
         log::error("strftime fails. tm_sec={}, tm_min={}, tm_hour={}", tstamp->tm_sec,
                    tstamp->tm_min, tstamp->tm_hour);
-        strlcpy(temptime, "UNKNOWN TIME", sizeof(temptime));
+        osi_strlcpy(temptime, "UNKNOWN TIME", sizeof(temptime));
       }
       snprintf(eventtime, sizeof(eventtime), "%s.%03ld", temptime,
                rssi_logs.timestamp.tv_nsec / 1000000);
diff --git a/system/bta/hearing_aid/hearing_aid_audio_source.cc b/system/bta/hearing_aid/hearing_aid_audio_source.cc
index 6ea05e7..d1d45d5 100644
--- a/system/bta/hearing_aid/hearing_aid_audio_source.cc
+++ b/system/bta/hearing_aid/hearing_aid_audio_source.cc
@@ -42,14 +42,14 @@
 
 using namespace bluetooth;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tUIPC_EVENT> : enum_formatter<tUIPC_EVENT> {};
 template <>
 struct formatter<tHEARING_AID_CTRL_ACK> : enum_formatter<tHEARING_AID_CTRL_ACK> {};
 template <>
 struct formatter<tHEARING_AID_CTRL_CMD> : enum_formatter<tHEARING_AID_CTRL_CMD> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace {
 #define CASE_RETURN_STR(const) \
diff --git a/system/bta/hf_client/bta_hf_client_api.cc b/system/bta/hf_client/bta_hf_client_api.cc
index 65c4fce..6c9ca68 100644
--- a/system/bta/hf_client/bta_hf_client_api.cc
+++ b/system/bta/hf_client/bta_hf_client_api.cc
@@ -186,7 +186,7 @@
   p_buf->uint32_val2 = val2;
 
   if (str) {
-    strlcpy(p_buf->str, str, BTA_HF_CLIENT_NUMBER_LEN + 1);
+    osi_strlcpy(p_buf->str, str, BTA_HF_CLIENT_NUMBER_LEN + 1);
     p_buf->str[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
   } else {
     p_buf->str[0] = '\0';
diff --git a/system/bta/hf_client/bta_hf_client_at.cc b/system/bta/hf_client/bta_hf_client_at.cc
index f002fbf..700e4ab 100644
--- a/system/bta/hf_client/bta_hf_client_at.cc
+++ b/system/bta/hf_client/bta_hf_client_at.cc
@@ -662,7 +662,7 @@
 
   memset(&evt, 0, sizeof(evt));
 
-  strlcpy(evt.operator_name.name, name, BTA_HF_CLIENT_OPERATOR_NAME_LEN + 1);
+  osi_strlcpy(evt.operator_name.name, name, BTA_HF_CLIENT_OPERATOR_NAME_LEN + 1);
   evt.operator_name.name[BTA_HF_CLIENT_OPERATOR_NAME_LEN] = '\0';
 
   evt.operator_name.bd_addr = client_cb->peer_addr;
@@ -684,7 +684,7 @@
 
   memset(&evt, 0, sizeof(evt));
 
-  strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+  osi_strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
   evt.number.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
 
   evt.number.bd_addr = client_cb->peer_addr;
@@ -706,7 +706,7 @@
 
   memset(&evt, 0, sizeof(evt));
 
-  strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+  osi_strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
   evt.number.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
 
   evt.number.bd_addr = client_cb->peer_addr;
@@ -759,7 +759,7 @@
 
   if (number) {
     evt.clcc.number_present = true;
-    strlcpy(evt.clcc.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+    osi_strlcpy(evt.clcc.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
     evt.clcc.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
   }
 
@@ -781,7 +781,7 @@
   tBTA_HF_CLIENT evt = {};
 
   evt.cnum.service = service;
-  strlcpy(evt.cnum.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+  osi_strlcpy(evt.cnum.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
   evt.cnum.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
 
   evt.cnum.bd_addr = client_cb->peer_addr;
@@ -791,7 +791,7 @@
 void bta_hf_client_unknown_response(tBTA_HF_CLIENT_CB* client_cb, const char* evt_buffer) {
   tBTA_HF_CLIENT evt = {};
 
-  strlcpy(evt.unknown.event_string, evt_buffer, BTA_HF_CLIENT_UNKNOWN_EVENT_LEN + 1);
+  osi_strlcpy(evt.unknown.event_string, evt_buffer, BTA_HF_CLIENT_UNKNOWN_EVENT_LEN + 1);
   evt.unknown.event_string[BTA_HF_CLIENT_UNKNOWN_EVENT_LEN] = '\0';
 
   evt.unknown.bd_addr = client_cb->peer_addr;
@@ -813,7 +813,7 @@
 
   memset(&evt, 0, sizeof(evt));
 
-  strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+  osi_strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
   evt.number.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
 
   evt.number.bd_addr = client_cb->peer_addr;
@@ -1517,7 +1517,7 @@
 
   char tmp_buf[BTA_HF_CLIENT_UNKNOWN_EVENT_LEN];
   if (evt_size < BTA_HF_CLIENT_UNKNOWN_EVENT_LEN) {
-    strlcpy(tmp_buf, start, evt_size);
+    osi_strlcpy(tmp_buf, start, evt_size);
     bta_hf_client_unknown_response(client_cb, tmp_buf);
     AT_CHECK_RN(end);
   } else {
diff --git a/system/bta/hh/bta_hh_int.h b/system/bta/hh/bta_hh_int.h
index 2f9f502..2e5e185 100644
--- a/system/bta/hh/bta_hh_int.h
+++ b/system/bta/hh/bta_hh_int.h
@@ -325,9 +325,9 @@
 void bta_hh_trace_dev_db(void);
 #endif
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_HH_SERVICE_STATE> : enum_formatter<tBTA_HH_SERVICE_STATE> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif
diff --git a/system/bta/include/bta_ag_api.h b/system/bta/include/bta_ag_api.h
index 04c5b66..5aa525c 100644
--- a/system/bta/include/bta_ag_api.h
+++ b/system/bta/include/bta_ag_api.h
@@ -647,9 +647,9 @@
 
 void BTA_AgSetActiveDevice(const RawAddress& active_device_addr);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_AG_RES> : enum_formatter<tBTA_AG_RES> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTA_AG_API_H */
diff --git a/system/bta/include/bta_api.h b/system/bta/include/bta_api.h
index fbdf277..a0b9e2c 100644
--- a/system/bta/include/bta_api.h
+++ b/system/bta/include/bta_api.h
@@ -847,13 +847,13 @@
 
 void DumpsysBtaDm(int fd);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_DM_SEARCH_EVT> : enum_formatter<tBTA_DM_SEARCH_EVT> {};
 template <>
 struct formatter<tBTA_DM_ACL_EVT> : enum_formatter<tBTA_DM_ACL_EVT> {};
 template <>
 struct formatter<tBTA_PREF_ROLES> : enum_formatter<tBTA_PREF_ROLES> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTA_API_H */
diff --git a/system/bta/include/bta_api_data_types.h b/system/bta/include/bta_api_data_types.h
index aa41dd3..1498374 100644
--- a/system/bta/include/bta_api_data_types.h
+++ b/system/bta/include/bta_api_data_types.h
@@ -63,7 +63,7 @@
   RawAddress bd_addr;
 } tBTA_DM_KEY_MISSING;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_STATUS> : enum_formatter<tBTA_STATUS> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/include/bta_gatt_api.h b/system/bta/include/bta_gatt_api.h
index ba60da6..8a8a91d 100644
--- a/system/bta/include/bta_gatt_api.h
+++ b/system/bta/include/bta_gatt_api.h
@@ -1044,9 +1044,9 @@
 // Adds bonded device for GATT server tracking service changes
 void BTA_GATTS_InitBonded(void);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_GATTC_EVT> : enum_formatter<tBTA_GATTC_EVT> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTA_GATT_API_H */
diff --git a/system/bta/include/bta_hearing_aid_api.h b/system/bta/include/bta_hearing_aid_api.h
index 19e89f5..7314d2e 100644
--- a/system/bta/include/bta_hearing_aid_api.h
+++ b/system/bta/include/bta_hearing_aid_api.h
@@ -282,7 +282,7 @@
   static void DebugDump(int fd);
 };
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<connection_update_status_t> : enum_formatter<connection_update_status_t> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/include/bta_hh_api.h b/system/bta/include/bta_hh_api.h
index e59f80a..976c7d2 100644
--- a/system/bta/include/bta_hh_api.h
+++ b/system/bta/include/bta_hh_api.h
@@ -561,8 +561,8 @@
  ******************************************************************************/
 void BTA_HhDump(int fd);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_HH_STATUS> : enum_formatter<tBTA_HH_STATUS> {};
-}  // namespace fmt
+}  // namespace std
 #endif /* BTA_HH_API_H */
diff --git a/system/bta/include/bta_jv_api.h b/system/bta/include/bta_jv_api.h
index 1809bf9..4b43cbc 100644
--- a/system/bta/include/bta_jv_api.h
+++ b/system/bta/include/bta_jv_api.h
@@ -148,10 +148,10 @@
   }
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_JV_CONN_STATE> : enum_formatter<tBTA_JV_CONN_STATE> {};
-}  // namespace fmt
+}  // namespace std
 
 /* JV Connection types */
 enum class tBTA_JV_CONN_TYPE {
@@ -171,10 +171,10 @@
   RETURN_UNKNOWN_TYPE_STRING(tBTA_JV_CONN_TYPE, type);
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_JV_CONN_TYPE> : enum_formatter<tBTA_JV_CONN_TYPE> {};
-}  // namespace fmt
+}  // namespace std
 
 enum tBTA_JV_EVT : uint16_t {
   /* Java I/F callback events */
diff --git a/system/bta/include/bta_sec_api.h b/system/bta/include/bta_sec_api.h
index 7446e72..a0f431f 100644
--- a/system/bta/include/bta_sec_api.h
+++ b/system/bta/include/bta_sec_api.h
@@ -484,7 +484,7 @@
  ******************************************************************************/
 void BTA_DmSirkConfirmDeviceReply(const RawAddress& bd_addr, bool accept);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_DM_SEC_EVT> : enum_formatter<tBTA_DM_SEC_EVT> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/jv/bta_jv_act.cc b/system/bta/jv/bta_jv_act.cc
index 20e57bd..9d47152 100644
--- a/system/bta/jv/bta_jv_act.cc
+++ b/system/bta/jv/bta_jv_act.cc
@@ -308,7 +308,7 @@
     return tBTA_JV_STATUS::FAILURE;
   }
   log::verbose("max_sess={}, curr_sess={}, p_pcb={}, user={}, state={}, jv handle=0x{:x}",
-               p_cb->max_sess, p_cb->curr_sess, fmt::ptr(p_pcb), p_pcb->rfcomm_slot_id,
+               p_cb->max_sess, p_cb->curr_sess, std::format_ptr(p_pcb), p_pcb->rfcomm_slot_id,
                p_pcb->state, p_pcb->handle);
 
   if (p_cb->curr_sess <= 0) {
@@ -319,7 +319,7 @@
     case BTA_JV_ST_CL_CLOSING:
     case BTA_JV_ST_SR_CLOSING:
       log::warn("return on closing, port state={}, scn={}, p_pcb={}, user_data={}", p_pcb->state,
-                p_cb->scn, fmt::ptr(p_pcb), p_pcb->rfcomm_slot_id);
+                p_cb->scn, std::format_ptr(p_pcb), p_pcb->rfcomm_slot_id);
       status = tBTA_JV_STATUS::FAILURE;
       return status;
     case BTA_JV_ST_CL_OPEN:
@@ -343,7 +343,7 @@
       log::warn(
               "failed, ignore port state= {}, scn={}, p_pcb= {}, jv handle=0x{:x}, "
               "port_handle={}, user_data={}",
-              p_pcb->state, p_cb->scn, fmt::ptr(p_pcb), p_pcb->handle, p_pcb->port_handle,
+              p_pcb->state, p_cb->scn, std::format_ptr(p_pcb), p_pcb->handle, p_pcb->port_handle,
               p_pcb->rfcomm_slot_id);
       status = tBTA_JV_STATUS::FAILURE;
       break;
@@ -560,7 +560,7 @@
         }
       }
       log::verbose("handle=0x{:x}, app_id={}, idx={}, BTA_JV_PM_MAX_NUM={}, pp_cb={}", jv_handle,
-                   app_id, i, BTA_JV_PM_MAX_NUM, fmt::ptr(pp_cb));
+                   app_id, i, BTA_JV_PM_MAX_NUM, std::format_ptr(pp_cb));
       break;
     }
   }
@@ -827,7 +827,8 @@
       tSDP_DISC_REC* p_sdp_rec = NULL;
       p_sdp_rec = get_legacy_stack_sdp_api()->db.SDP_FindServiceUUIDInDb(
               p_bta_jv_cfg->p_sdp_db, bta_jv_cb.sdp_cb.uuid, p_sdp_rec);
-      log::verbose("bta_jv_cb.uuid={} p_sdp_rec={}", bta_jv_cb.sdp_cb.uuid, fmt::ptr(p_sdp_rec));
+      log::verbose("bta_jv_cb.uuid={} p_sdp_rec={}", bta_jv_cb.sdp_cb.uuid,
+                   std::format_ptr(p_sdp_rec));
       if (p_sdp_rec && get_legacy_stack_sdp_api()->record.SDP_FindProtocolListElemInRec(
                                p_sdp_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
         bta_jv = {
@@ -1338,7 +1339,8 @@
 static int bta_jv_port_data_co_cback(uint16_t port_handle, uint8_t* buf, uint16_t len, int type) {
   tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
   tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
-  log::verbose("p_cb={}, p_pcb={}, len={}, type={}", fmt::ptr(p_cb), fmt::ptr(p_pcb), len, type);
+  log::verbose("p_cb={}, p_pcb={}, len={}, type={}", std::format_ptr(p_cb), std::format_ptr(p_pcb),
+               len, type);
   if (p_pcb != NULL) {
     switch (type) {
       case DATA_CO_CALLBACK_TYPE_INCOMING:
@@ -1588,13 +1590,13 @@
   uint16_t lcid;
   log::verbose("code={}, port_handle={}", code, port_handle);
   if (NULL == p_cb || NULL == p_cb->p_cback) {
-    log::error("p_cb={}, p_cb->p_cback={}", fmt::ptr(p_cb),
-               fmt::ptr(p_cb ? p_cb->p_cback : nullptr));
+    log::error("p_cb={}, p_cb->p_cback={}", std::format_ptr(p_cb),
+               std::format_ptr(p_cb ? p_cb->p_cback : nullptr));
     return;
   }
   uint32_t rfcomm_slot_id = p_pcb->rfcomm_slot_id;
   log::verbose("code={}, port_handle=0x{:x}, handle=0x{:x}, p_pcb{}, user={}", code, port_handle,
-               p_cb->handle, fmt::ptr(p_pcb), p_pcb->rfcomm_slot_id);
+               p_cb->handle, std::format_ptr(p_pcb), p_pcb->rfcomm_slot_id);
 
   int status = PORT_CheckConnection(port_handle, &rem_bda, &lcid);
   int failed = true;
@@ -1659,8 +1661,8 @@
   tBTA_JV evt_data;
 
   if (NULL == p_cb || NULL == p_cb->p_cback) {
-    log::error("p_cb={}, p_cb->p_cback={}", fmt::ptr(p_cb),
-               fmt::ptr(p_cb ? p_cb->p_cback : nullptr));
+    log::error("p_cb={}, p_cb->p_cback={}", std::format_ptr(p_cb),
+               std::format_ptr(p_cb ? p_cb->p_cback : nullptr));
     return;
   }
 
@@ -1861,7 +1863,7 @@
   if (!find_rfc_pcb(rfcomm_slot_id, &p_cb, &p_pcb)) {
     return;
   }
-  log::verbose("p_pcb={}, p_pcb->port_handle={}", fmt::ptr(p_pcb), p_pcb->port_handle);
+  log::verbose("p_pcb={}, p_pcb->port_handle={}", std::format_ptr(p_pcb), p_pcb->port_handle);
   bta_jv_free_rfc_cb(p_cb, p_pcb);
 }
 
@@ -1969,7 +1971,7 @@
  ******************************************************************************/
 static void bta_jv_pm_state_change(tBTA_JV_PM_CB* p_cb, const tBTA_JV_CONN_STATE state) {
   log::verbose("p_cb={}, handle=0x{:x}, busy/idle_state={}, app_id={}, conn_state={}",
-               fmt::ptr(p_cb), p_cb->handle, p_cb->state, p_cb->app_id,
+               std::format_ptr(p_cb), p_cb->handle, p_cb->state, p_cb->app_id,
                bta_jv_conn_state_text(state));
 
   switch (state) {
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index bcab997..8d5fbb1 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -213,10 +213,10 @@
   return os;
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<AudioState> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
 
 namespace {
 void le_audio_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
@@ -828,7 +828,7 @@
       }
     }
 
-    log::debug("New group {}, id: {}", fmt::ptr(new_group), new_group->group_id_);
+    log::debug("New group {}, id: {}", std::format_ptr(new_group), new_group->group_id_);
 
     /* If device was in the group and it was not removed by the application,
      * lets do it now
@@ -869,8 +869,8 @@
       log::debug("group is null");
       return;
     }
-    log::debug("Group {}, id: {}, size: {}, is cig_state {}", fmt::ptr(group), group->group_id_,
-               group->Size(), ToString(group->cig.GetState()));
+    log::debug("Group {}, id: {}, size: {}, is cig_state {}", std::format_ptr(group),
+               group->group_id_, group->Size(), ToString(group->cig.GetState()));
     if (group->IsEmpty() && (group->cig.GetState() == bluetooth::le_audio::types::CigState::NONE)) {
       lastNotifiedGroupStreamStatusMap_.erase(group->group_id_);
       aseGroups_.Remove(group->group_id_);
@@ -2309,6 +2309,9 @@
 
     leAudioDevice->conn_id_ = conn_id;
     leAudioDevice->mtu_ = mtu;
+    if (com::android::bluetooth::flags::gatt_queue_cleanup_connected()) {
+      BtaGattQueue::Clean(conn_id);
+    }
 
     /* Remove device from the background connect (it might be either Allow list
      * or TA) and add it again with reconnection_mode_. In case it is TA, we are
@@ -2688,7 +2691,8 @@
      * issues
      */
     if (group == nullptr || !group->IsEnabled()) {
-      log::error("Group id {} ({}) disabled or null", leAudioDevice->group_id_, fmt::ptr(group));
+      log::error("Group id {} ({}) disabled or null", leAudioDevice->group_id_,
+                 std::format_ptr(group));
       return;
     }
 
@@ -2802,7 +2806,7 @@
   void OnServiceChangeEvent(const RawAddress& address) {
     LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(address);
     if (!leAudioDevice) {
-      log::warn("Skipping unknown leAudioDevice {} ({})", address, fmt::ptr(leAudioDevice));
+      log::warn("Skipping unknown leAudioDevice {} ({})", address, std::format_ptr(leAudioDevice));
       return;
     }
 
@@ -2861,7 +2865,7 @@
     LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(address);
     if (!leAudioDevice || (leAudioDevice->conn_id_ == GATT_INVALID_CONN_ID)) {
       log::verbose("skipping unknown leAudioDevice, address {} ({})", address,
-                   fmt::ptr(leAudioDevice));
+                   std::format_ptr(leAudioDevice));
       return;
     }
 
diff --git a/system/bta/le_audio/client_parser.h b/system/bta/le_audio/client_parser.h
index 1aaf4b3..e9e4756 100644
--- a/system/bta/le_audio/client_parser.h
+++ b/system/bta/le_audio/client_parser.h
@@ -22,6 +22,7 @@
 
 #pragma once
 
+#include <memory>
 #include <vector>
 
 #include "le_audio_types.h"
diff --git a/system/bta/le_audio/codec_interface.h b/system/bta/le_audio/codec_interface.h
index 7e99fc9..8841bba 100644
--- a/system/bta/le_audio/codec_interface.h
+++ b/system/bta/le_audio/codec_interface.h
@@ -73,8 +73,8 @@
 
 }  // namespace bluetooth::le_audio
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::le_audio::CodecInterface::Status>
     : enum_formatter<bluetooth::le_audio::CodecInterface::Status> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/le_audio/codec_manager.cc b/system/bta/le_audio/codec_manager.cc
index eb0efb5..9977fec 100644
--- a/system/bta/le_audio/codec_manager.cc
+++ b/system/bta/le_audio/codec_manager.cc
@@ -211,8 +211,9 @@
   bool UpdateActiveUnicastAudioHalClient(LeAudioSourceAudioHalClient* source_unicast_client,
                                          LeAudioSinkAudioHalClient* sink_unicast_client,
                                          bool is_active) {
-    log::debug("local_source: {}, local_sink: {}, is_active: {}", fmt::ptr(source_unicast_client),
-               fmt::ptr(sink_unicast_client), is_active);
+    log::debug("local_source: {}, local_sink: {}, is_active: {}",
+               std::format_ptr(source_unicast_client), std::format_ptr(sink_unicast_client),
+               is_active);
 
     if (source_unicast_client == nullptr && sink_unicast_client == nullptr) {
       return false;
@@ -221,13 +222,13 @@
     if (is_active) {
       if (source_unicast_client && unicast_local_source_hal_client != nullptr) {
         log::error("Trying to override previous source hal client {}",
-                   fmt::ptr(unicast_local_source_hal_client));
+                   std::format_ptr(unicast_local_source_hal_client));
         return false;
       }
 
       if (sink_unicast_client && unicast_local_sink_hal_client != nullptr) {
         log::error("Trying to override previous sink hal client {}",
-                   fmt::ptr(unicast_local_sink_hal_client));
+                   std::format_ptr(unicast_local_sink_hal_client));
         return false;
       }
 
@@ -243,14 +244,16 @@
     }
 
     if (source_unicast_client && source_unicast_client != unicast_local_source_hal_client) {
-      log::error("local source session does not match {} != {}", fmt::ptr(source_unicast_client),
-                 fmt::ptr(unicast_local_source_hal_client));
+      log::error("local source session does not match {} != {}",
+                 std::format_ptr(source_unicast_client),
+                 std::format_ptr(unicast_local_source_hal_client));
       return false;
     }
 
     if (sink_unicast_client && sink_unicast_client != unicast_local_sink_hal_client) {
-      log::error("local source session does not match {} != {}", fmt::ptr(sink_unicast_client),
-                 fmt::ptr(unicast_local_sink_hal_client));
+      log::error("local source session does not match {} != {}",
+                 std::format_ptr(sink_unicast_client),
+                 std::format_ptr(unicast_local_sink_hal_client));
       return false;
     }
 
@@ -267,7 +270,8 @@
 
   bool UpdateActiveBroadcastAudioHalClient(LeAudioSourceAudioHalClient* source_broadcast_client,
                                            bool is_active) {
-    log::debug("local_source: {},is_active: {}", fmt::ptr(source_broadcast_client), is_active);
+    log::debug("local_source: {},is_active: {}", std::format_ptr(source_broadcast_client),
+               is_active);
 
     if (source_broadcast_client == nullptr) {
       return false;
@@ -276,7 +280,7 @@
     if (is_active) {
       if (broadcast_local_source_hal_client != nullptr) {
         log::error("Trying to override previous source hal client {}",
-                   fmt::ptr(broadcast_local_source_hal_client));
+                   std::format_ptr(broadcast_local_source_hal_client));
         return false;
       }
       broadcast_local_source_hal_client = source_broadcast_client;
@@ -284,8 +288,9 @@
     }
 
     if (source_broadcast_client != broadcast_local_source_hal_client) {
-      log::error("local source session does not match {} != {}", fmt::ptr(source_broadcast_client),
-                 fmt::ptr(broadcast_local_source_hal_client));
+      log::error("local source session does not match {} != {}",
+                 std::format_ptr(source_broadcast_client),
+                 std::format_ptr(broadcast_local_source_hal_client));
       return false;
     }
 
diff --git a/system/bta/le_audio/codec_manager.h b/system/bta/le_audio/codec_manager.h
index b41bb3b..1a86a9d 100644
--- a/system/bta/le_audio/codec_manager.h
+++ b/system/bta/le_audio/codec_manager.h
@@ -145,8 +145,8 @@
                          const CodecManager::UnicastConfigurationRequirements& req);
 }  // namespace bluetooth::le_audio
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::le_audio::CodecManager::UnicastConfigurationRequirements>
     : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/le_audio/device_groups.cc b/system/bta/le_audio/device_groups.cc
index f1048b4..192f3e9 100644
--- a/system/bta/le_audio/device_groups.cc
+++ b/system/bta/le_audio/device_groups.cc
@@ -139,7 +139,7 @@
 }
 
 void LeAudioDeviceGroup::ClearSinksFromConfiguration(void) {
-  log::info("Group {}, group_id {}", fmt::ptr(this), group_id_);
+  log::info("Group {}, group_id {}", std::format_ptr(this), group_id_);
 
   auto direction = types::kLeAudioDirectionSink;
   stream_conf.stream_params.get(direction).clear();
@@ -147,7 +147,7 @@
 }
 
 void LeAudioDeviceGroup::ClearSourcesFromConfiguration(void) {
-  log::info("Group {}, group_id {}", fmt::ptr(this), group_id_);
+  log::info("Group {}, group_id {}", std::format_ptr(this), group_id_);
 
   auto direction = types::kLeAudioDirectionSource;
   stream_conf.stream_params.get(direction).clear();
@@ -1173,7 +1173,7 @@
 }
 
 uint8_t LeAudioDeviceGroup::CigConfiguration::GetFirstFreeCisId(CisType cis_type) const {
-  log::info("Group: {}, group_id: {} cis_type: {}", fmt::ptr(group_), group_->group_id_,
+  log::info("Group: {}, group_id: {} cis_type: {}", std::format_ptr(group_), group_->group_id_,
             static_cast<int>(cis_type));
   for (size_t id = 0; id < cises.size(); id++) {
     if (cises[id].addr.IsEmpty() && cises[id].type == cis_type) {
@@ -1243,7 +1243,7 @@
 }
 
 void LeAudioDeviceGroup::CigConfiguration::GenerateCisIds(LeAudioContextType context_type) {
-  log::info("Group {}, group_id: {}, context_type: {}", fmt::ptr(group_), group_->group_id_,
+  log::info("Group {}, group_id: {}, context_type: {}", std::format_ptr(group_), group_->group_id_,
             bluetooth::common::ToString(context_type));
 
   if (cises.size() > 0) {
@@ -1439,7 +1439,7 @@
 
 void LeAudioDeviceGroup::AssignCisConnHandlesToAses(LeAudioDevice* leAudioDevice) {
   log::assert_that(leAudioDevice, "Invalid device");
-  log::info("group: {}, group_id: {}, device: {}", fmt::ptr(this), group_id_,
+  log::info("group: {}, group_id: {}, device: {}", std::format_ptr(this), group_id_,
             leAudioDevice->address_);
 
   /* Assign all CIS connection handles to ases */
@@ -1470,7 +1470,7 @@
   LeAudioDevice* leAudioDevice = GetFirstActiveDevice();
   log::assert_that(leAudioDevice, "Shouldn't be called without an active device.");
 
-  log::info("Group {}, group_id {}", fmt::ptr(this), group_id_);
+  log::info("Group {}, group_id {}", std::format_ptr(this), group_id_);
 
   /* Assign all CIS connection handles to ases */
   for (; leAudioDevice != nullptr; leAudioDevice = GetNextActiveDevice(leAudioDevice)) {
@@ -1482,7 +1482,7 @@
                                                        uint16_t conn_handle) {
   log::assert_that(leAudioDevice, "Invalid device");
 
-  log::info("Group {}, group_id {}, device: {}, conn_handle: {:#x}", fmt::ptr(group_),
+  log::info("Group {}, group_id {}, device: {}, conn_handle: {:#x}", std::format_ptr(group_),
             group_->group_id_, leAudioDevice->address_, conn_handle);
 
   for (struct bluetooth::le_audio::types::cis& cis_entry : cises) {
@@ -1747,8 +1747,9 @@
 
     auto const max_required_device_cnt = NumOfAvailableForDirection(direction);
     auto required_device_cnt = max_required_device_cnt;
-    uint8_t active_ase_cnt = 0;
+    log::debug("Maximum {} device(s) required for {}", max_required_device_cnt, direction_str);
 
+    uint8_t active_ase_cnt = 0;
     auto configuration_closure = [&](LeAudioDevice* dev) -> void {
       /* For the moment, we configure only connected devices and when it is
        * ready to stream i.e. All ASEs are discovered and dev is reported as
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index b5e9dee..e30ecbd 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -291,16 +291,17 @@
                                   AudioLocations& group_audio_locations_memo,
                                   const AudioContexts& metadata_context_types,
                                   const std::vector<uint8_t>& ccid_lists, bool reuse_cis_id) {
+  auto direction_str = (direction == types::kLeAudioDirectionSink ? "Sink" : "Source");
   /* First try to use the already configured ASE */
   auto ase = GetFirstActiveAseByDirection(direction);
   if (ase) {
-    log::info("{}, using an already active ASE id={}", address_, ase->id);
+    log::info("{}, using an already active {} ASE id={}", address_, direction_str, ase->id);
   } else {
     ase = GetFirstInactiveAse(direction, reuse_cis_id);
   }
 
   if (!ase) {
-    log::error("{}, unable to find an ASE to configure", address_);
+    log::error("{}, unable to find a {} ASE to configure", address_, direction_str);
     PrintDebugState();
     return false;
   }
@@ -339,6 +340,8 @@
     auto const& ase_cfg = ase_configs.at(i);
     if (utils::IsCodecUsingLtvFormat(ase_cfg.codec.id) &&
         !utils::GetConfigurationSupportedPac(pacs, ase_cfg.codec)) {
+      log::error("{}, No {} PAC found matching codec: {}. Stop the activation.", address_,
+                 direction_str, common::ToString(ase_cfg.codec));
       return false;
     }
   }
@@ -356,12 +359,15 @@
   uint8_t max_required_ase_per_dev =
           ase_configs.size() / num_of_devices + (ase_configs.size() % num_of_devices);
   int needed_ase = std::min((int)(max_required_ase_per_dev), (int)(ase_configs.size()));
+  log::debug("{}, {} {} ASE(s) required for this configuration.", address_, needed_ase,
+             direction_str);
 
   for (int i = 0; i < needed_ase; ++i) {
     auto const& ase_cfg = ase_configs.at(i);
     if (utils::IsCodecUsingLtvFormat(ase_cfg.codec.id) &&
         !utils::GetConfigurationSupportedPac(pacs, ase_cfg.codec)) {
-      log::error("{}, no matching PAC found. Stop the activation.", address_);
+      log::error("{}, No {} PAC found matching codec: {}. Stop the activation.", address_,
+                 direction_str, common::ToString(ase_cfg.codec));
       return false;
     }
   }
@@ -429,8 +435,8 @@
     log::debug(
             "device={}, activated ASE id={}, direction={}, max_sdu_size={}, "
             "cis_id={}, target_latency={}",
-            address_, ase->id, direction == 1 ? "snk" : "src", ase->qos_config.max_sdu_size,
-            ase->cis_id, ase_cfg.qos.target_latency);
+            address_, ase->id, direction_str, ase->qos_config.max_sdu_size, ase->cis_id,
+            ase_cfg.qos.target_latency);
 
     /* Try to use the already active ASE */
     ase = GetNextActiveAseWithSameDirection(ase);
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index c2398ff..2c7ecbb 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -1318,7 +1318,7 @@
                                   log::info(
                                           ", sink ase to delete. Cis handle: {}, ase pointer: "
                                           "{}",
-                                          (int)(pair.first), fmt::ptr(+ases.sink));
+                                          (int)(pair.first), std::format_ptr(+ases.sink));
                                   if (ases.sink) {
                                     stream_conf->stream_params.sink.num_of_devices--;
                                     stream_conf->stream_params.sink.num_of_channels -=
@@ -1342,7 +1342,7 @@
                                   auto ases = leAudioDevice->GetAsesByCisConnHdl(pair.first);
 
                                   log::info(", source to delete. Cis handle: {}, ase pointer: {}",
-                                            (int)(pair.first), fmt::ptr(ases.source));
+                                            (int)(pair.first), std::format_ptr(ases.source));
                                   if (ases.source) {
                                     stream_conf->stream_params.source.num_of_devices--;
                                     stream_conf->stream_params.source.num_of_channels -=
@@ -1378,7 +1378,7 @@
                                    log::info(
                                            ", sink ase to delete. Cis handle: {}, ase "
                                            "pointer: {}",
-                                           (int)(pair.first), fmt::ptr(+ases.sink));
+                                           (int)(pair.first), std::format_ptr(+ases.sink));
                                    if (ases.sink) {
                                      stream_conf->stream_params.sink.num_of_devices--;
                                      stream_conf->stream_params.sink.num_of_channels -=
@@ -1403,7 +1403,7 @@
                                    log::info(
                                            ", source to delete. Cis handle: {}, ase pointer: "
                                            "{}",
-                                           (int)(pair.first), fmt::ptr(+ases.source));
+                                           (int)(pair.first), std::format_ptr(+ases.source));
                                    if (ases.source) {
                                      stream_conf->stream_params.source.num_of_devices--;
                                      stream_conf->stream_params.source.num_of_channels -=
diff --git a/system/bta/le_audio/le_audio_health_status.h b/system/bta/le_audio/le_audio_health_status.h
index 62b6a17..95f0aba 100644
--- a/system/bta/le_audio/le_audio_health_status.h
+++ b/system/bta/le_audio/le_audio_health_status.h
@@ -159,11 +159,11 @@
 }
 }  // namespace bluetooth::le_audio
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::le_audio::LeAudioHealthDeviceStatType>
     : enum_formatter<bluetooth::le_audio::LeAudioHealthDeviceStatType> {};
 template <>
 struct formatter<bluetooth::le_audio::LeAudioHealthGroupStatType>
     : enum_formatter<bluetooth::le_audio::LeAudioHealthGroupStatType> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index 85bb1be..a0c5f36 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -28,6 +28,7 @@
 #include <bit>
 #include <bitset>
 #include <map>
+#include <memory>
 #include <optional>
 #include <string>
 #include <variant>
@@ -1329,7 +1330,7 @@
 uint8_t GetMaxCodecFramesPerSduFromPac(const types::acs_ac_record* pac_record);
 }  // namespace bluetooth::le_audio
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::le_audio::DsaMode> : enum_formatter<bluetooth::le_audio::DsaMode> {};
 template <>
@@ -1338,4 +1339,4 @@
 template <>
 struct formatter<bluetooth::le_audio::types::LeAudioConfigurationStrategy>
     : enum_formatter<bluetooth::le_audio::types::LeAudioConfigurationStrategy> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/bta/le_audio/le_audio_utils.cc b/system/bta/le_audio/le_audio_utils.cc
index 14b123c..3b89926 100644
--- a/system/bta/le_audio/le_audio_utils.cc
+++ b/system/bta/le_audio/le_audio_utils.cc
@@ -34,7 +34,7 @@
 using bluetooth::le_audio::types::AudioContexts;
 using bluetooth::le_audio::types::LeAudioContextType;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<audio_usage_t> : enum_formatter<audio_usage_t> {};
 template <>
@@ -43,7 +43,7 @@
 struct formatter<audio_source_t> : enum_formatter<audio_source_t> {};
 template <>
 struct formatter<audio_devices_t> : enum_formatter<audio_devices_t> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace bluetooth::le_audio {
 namespace utils {
@@ -584,7 +584,7 @@
     return false;
   }
 
-  log::debug(": Settings for format: 0x{:#02x} ", codec_id.coding_format);
+  log::debug("Verifying coding format: {:#02x} ", codec_id.coding_format);
 
   if (utils::IsCodecUsingLtvFormat(codec_id)) {
     log::assert_that(!pac.codec_spec_caps.IsEmpty(),
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 1f01b3b..cc59c4d 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -541,7 +541,7 @@
                      ToString(group->cig.GetState()));
 
     group->cig.SetState(CigState::CREATED);
-    log::info("Group: {}, id: {} cig state: {}, number of cis handles: {}", fmt::ptr(group),
+    log::info("Group: {}, id: {} cig state: {}, number of cis handles: {}", std::format_ptr(group),
               group->group_id_, ToString(group->cig.GetState()),
               static_cast<int>(conn_handles.size()));
 
@@ -793,17 +793,17 @@
   }
 
   void RemoveCigForGroup(LeAudioDeviceGroup* group) {
-    log::debug("Group: {}, id: {} cig state: {}", fmt::ptr(group), group->group_id_,
+    log::debug("Group: {}, id: {} cig state: {}", std::format_ptr(group), group->group_id_,
                ToString(group->cig.GetState()));
     if (group->cig.GetState() != CigState::CREATED) {
-      log::warn("Group: {}, id: {} cig state: {} cannot be removed", fmt::ptr(group),
+      log::warn("Group: {}, id: {} cig state: {} cannot be removed", std::format_ptr(group),
                 group->group_id_, ToString(group->cig.GetState()));
       return;
     }
 
     group->cig.SetState(CigState::REMOVING);
     IsoManager::GetInstance()->RemoveCig(group->group_id_);
-    log::debug("Group: {}, id: {} cig state: {}", fmt::ptr(group), group->group_id_,
+    log::debug("Group: {}, id: {} cig state: {}", std::format_ptr(group), group->group_id_,
                ToString(group->cig.GetState()));
     log_history_->AddLogHistory(kLogStateMachineTag, group->group_id_, RawAddress::kEmpty,
                                 kLogCigRemoveOp);
@@ -1501,12 +1501,12 @@
     uint8_t packing, framing, sca;
     std::vector<EXT_CIS_CFG> cis_cfgs;
 
-    log::debug("Group: {}, id: {} cig state: {}", fmt::ptr(group), group->group_id_,
+    log::debug("Group: {}, id: {} cig state: {}", std::format_ptr(group), group->group_id_,
                ToString(group->cig.GetState()));
 
     if (group->cig.GetState() != CigState::NONE) {
-      log::warn("Group {}, id: {} has invalid cig state: {}", fmt::ptr(group), group->group_id_,
-                ToString(group->cig.GetState()));
+      log::warn("Group {}, id: {} has invalid cig state: {}", std::format_ptr(group),
+                group->group_id_, ToString(group->cig.GetState()));
       return false;
     }
 
@@ -1620,7 +1620,7 @@
 
     group->cig.SetState(CigState::CREATING);
     IsoManager::GetInstance()->CreateCig(group->group_id_, std::move(param));
-    log::debug("Group: {}, id: {} cig state: {}", fmt::ptr(group), group->group_id_,
+    log::debug("Group: {}, id: {} cig state: {}", std::format_ptr(group), group->group_id_,
                ToString(group->cig.GetState()));
     return true;
   }
diff --git a/system/bta/sys/bta_sys.h b/system/bta/sys/bta_sys.h
index e1bca2f..e1a9e5e 100644
--- a/system/bta/sys/bta_sys.h
+++ b/system/bta/sys/bta_sys.h
@@ -260,11 +260,11 @@
 void bta_sys_add_cust_uuid(const tBTA_CUSTOM_UUID& curr);
 void bta_sys_remove_cust_uuid(const tBTA_CUSTOM_UUID& curr);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTA_SYS_ID> : enum_formatter<tBTA_SYS_ID> {};
 template <>
 struct formatter<tBTA_SYS_CONN_STATUS> : enum_formatter<tBTA_SYS_CONN_STATUS> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTA_SYS_H */
diff --git a/system/bta/test/bta_api_test.cc b/system/bta/test/bta_api_test.cc
index 0075fa5..ccf8bdf 100644
--- a/system/bta/test/bta_api_test.cc
+++ b/system/bta/test/bta_api_test.cc
@@ -18,10 +18,10 @@
 
 #include <base/functional/bind.h>
 #include <base/location.h>
-#include <fmt/format.h>
 #include <gtest/gtest.h>
 
 #include <cstdint>
+#include <format>
 #include <utility>
 #include <vector>
 
@@ -47,7 +47,7 @@
   for (const auto& status : statuses) {
     ASSERT_STREQ(status.second.c_str(), bta_status_text(status.first).c_str());
   }
-  auto unknown = fmt::format("UNKNOWN[{}]", std::numeric_limits<uint8_t>::max());
+  auto unknown = std::format("UNKNOWN[{}]", std::numeric_limits<uint8_t>::max());
   ASSERT_STREQ(
           unknown.c_str(),
           bta_status_text(static_cast<tBTA_STATUS>(std::numeric_limits<uint8_t>::max())).c_str());
diff --git a/system/bta/test/bta_disc_test.cc b/system/bta/test/bta_disc_test.cc
index 0979226..72e446c 100644
--- a/system/bta/test/bta_disc_test.cc
+++ b/system/bta/test/bta_disc_test.cc
@@ -20,7 +20,6 @@
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
 #include <flag_macros.h>
-#include <fmt/format.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <sys/socket.h>
diff --git a/system/bta/test/bta_dm_test.cc b/system/bta/test/bta_dm_test.cc
index e4d2fb9..91a8390 100644
--- a/system/bta/test/bta_dm_test.cc
+++ b/system/bta/test/bta_dm_test.cc
@@ -19,10 +19,10 @@
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
 #include <flag_macros.h>
-#include <fmt/format.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <format>
 #include <string>
 
 #include "bta/dm/bta_dm_device_search_int.h"
@@ -427,11 +427,10 @@
   for (const auto& event : events) {
     ASSERT_STREQ(event.second.c_str(), bta_dm_search_evt_text(event.first).c_str());
   }
-  ASSERT_STREQ(
-          fmt::format("UNKNOWN[{}]", std::numeric_limits<uint8_t>::max()).c_str(),
-          bta_dm_search_evt_text(
-                  static_cast<tBTA_DM_SEARCH_EVT>(std::numeric_limits<uint8_t>::max()))
-                  .c_str());
+  ASSERT_STREQ(std::format("UNKNOWN[{}]", std::numeric_limits<uint8_t>::max()).c_str(),
+               bta_dm_search_evt_text(
+                       static_cast<tBTA_DM_SEARCH_EVT>(std::numeric_limits<uint8_t>::max()))
+                       .c_str());
 }
 
 TEST_F(BtaDmTest, bta_dm_remote_name_cmpl) {
diff --git a/system/bta/test/bta_gatt_client_test.cc b/system/bta/test/bta_gatt_client_test.cc
index 5dca105..3c19614 100644
--- a/system/bta/test/bta_gatt_client_test.cc
+++ b/system/bta/test/bta_gatt_client_test.cc
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include <fmt/format.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <sys/socket.h>
 
+#include <format>
 #include <string>
 
 #include "bta/dm/bta_dm_gatt_client.h"
@@ -54,11 +54,11 @@
   };
 
   // C string
-  gatt_history_callback(fmt::format("{}", a[0].c_str()));
+  gatt_history_callback(std::format("{}", a[0].c_str()));
   // Cpp string
   gatt_history_callback(a[1]);
   // Third entry for "fun"
-  gatt_history_callback(fmt::format("{}", a[2].c_str()));
+  gatt_history_callback(std::format("{}", a[2].c_str()));
 
   std::vector<bluetooth::common::TimestampedEntry<std::string>> history =
           bluetooth::legacy::testing::PullCopyOfGattHistory();
diff --git a/system/bta/test/bta_sec_test.cc b/system/bta/test/bta_sec_test.cc
index ddf83e5..20b82cc 100644
--- a/system/bta/test/bta_sec_test.cc
+++ b/system/bta/test/bta_sec_test.cc
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <fmt/format.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <sys/socket.h>
diff --git a/system/btcore/src/property.cc b/system/btcore/src/property.cc
index 5592b14..0379229 100644
--- a/system/btcore/src/property.cc
+++ b/system/btcore/src/property.cc
@@ -205,7 +205,7 @@
 
   property->val = osi_calloc(len + 1);
   if (type == BT_PROPERTY_BDNAME) {
-    strlcpy((char*)property->val, (const char*)val, len);
+    osi_strlcpy((char*)property->val, (const char*)val, len);
   } else {
     memcpy(property->val, val, len);
   }
diff --git a/system/btif/co/bta_hh_co.cc b/system/btif/co/bta_hh_co.cc
index 12735b6..52cdb17 100644
--- a/system/btif/co/bta_hh_co.cc
+++ b/system/btif/co/bta_hh_co.cc
@@ -986,7 +986,7 @@
 
   // Create and send hid descriptor to kernel
   ev.type = UHID_CREATE2;
-  strlcpy((char*)ev.u.create2.name, dev_name, sizeof(ev.u.create2.name));
+  osi_strlcpy((char*)ev.u.create2.name, dev_name, sizeof(ev.u.create2.name));
   // TODO (b/258090765) fix: ToString -> ToColonSepHexString
   snprintf((char*)ev.u.create2.uniq, sizeof(ev.u.create2.uniq), "%s",
            p_dev->link_spec.addrt.bda.ToString().c_str());
diff --git a/system/btif/co/bta_pan_co.cc b/system/btif/co/bta_pan_co.cc
index 5581492..2928373 100644
--- a/system/btif/co/bta_pan_co.cc
+++ b/system/btif/co/bta_pan_co.cc
@@ -122,7 +122,7 @@
     log::error("cannot find pan connection");
     return;
   } else if (conn->state != PAN_STATE_OPEN) {
-    log::error("conn is not opened, conn:{}, conn->state:{}", fmt::ptr(conn), conn->state);
+    log::error("conn is not opened, conn:{}, conn->state:{}", std::format_ptr(conn), conn->state);
     return;
   }
 
diff --git a/system/btif/include/btif_a2dp_sink.h b/system/btif/include/btif_a2dp_sink.h
index 2c71f65..81d4bff 100644
--- a/system/btif/include/btif_a2dp_sink.h
+++ b/system/btif/include/btif_a2dp_sink.h
@@ -133,9 +133,9 @@
 // Get audio track handle
 void* btif_a2dp_sink_get_audio_track(void);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<btif_a2dp_sink_focus_state_t> : enum_formatter<btif_a2dp_sink_focus_state_t> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* BTIF_A2DP_SINK_H */
diff --git a/system/btif/include/btif_bqr.h b/system/btif/include/btif_bqr.h
index 38bfa8b..e1ec64c 100644
--- a/system/btif/include/btif_bqr.h
+++ b/system/btif/include/btif_bqr.h
@@ -530,10 +530,10 @@
 }  // namespace bqr
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::bqr::BqrReportAction>
     : enum_formatter<bluetooth::bqr::BqrReportAction> {};
 template <>
 struct formatter<bluetooth::bqr::BqrVseSubEvt> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/btif/include/btif_hd.h b/system/btif/include/btif_hd.h
index 0558118..feaa678 100644
--- a/system/btif/include/btif_hd.h
+++ b/system/btif/include/btif_hd.h
@@ -45,9 +45,9 @@
 void btif_hd_remove_device(RawAddress bd_addr);
 void btif_hd_service_registration();
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<BTIF_HD_STATUS> : enum_formatter<BTIF_HD_STATUS> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif
diff --git a/system/btif/include/btif_hh.h b/system/btif/include/btif_hh.h
index a0774c5..c55dded 100644
--- a/system/btif/include/btif_hh.h
+++ b/system/btif/include/btif_hh.h
@@ -173,9 +173,9 @@
 void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH* p_data);
 }  // namespace bluetooth::legacy::testing
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<BTIF_HH_STATUS> : enum_formatter<BTIF_HH_STATUS> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif
diff --git a/system/btif/src/btif_a2dp.cc b/system/btif/src/btif_a2dp.cc
index f7632bf..63e6c6c 100644
--- a/system/btif/src/btif_a2dp.cc
+++ b/system/btif/src/btif_a2dp.cc
@@ -52,7 +52,7 @@
 
 bool btif_a2dp_on_started(const RawAddress& peer_addr, tBTA_AV_START* p_av_start,
                           const A2dpType local_a2dp_type) {
-  log::info("## ON A2DP STARTED ## peer {} p_av_start:{}", peer_addr, fmt::ptr(p_av_start));
+  log::info("## ON A2DP STARTED ## peer {} p_av_start:{}", peer_addr, std::format_ptr(p_av_start));
 
   if (p_av_start == NULL) {
     auto status = BluetoothAudioStatus::SUCCESS;
@@ -95,7 +95,7 @@
 }
 
 void btif_a2dp_on_stopped(tBTA_AV_SUSPEND* p_av_suspend, const A2dpType local_a2dp_type) {
-  log::info("## ON A2DP STOPPED ## p_av_suspend={}", fmt::ptr(p_av_suspend));
+  log::info("## ON A2DP STOPPED ## p_av_suspend={}", std::format_ptr(p_av_suspend));
 
   const uint8_t peer_type_sep = btif_av_get_peer_sep(local_a2dp_type);
   if (peer_type_sep == AVDT_TSEP_SRC) {
@@ -111,7 +111,7 @@
 }
 
 void btif_a2dp_on_suspended(tBTA_AV_SUSPEND* p_av_suspend, const A2dpType local_a2dp_type) {
-  log::info("## ON A2DP SUSPENDED ## p_av_suspend={}", fmt::ptr(p_av_suspend));
+  log::info("## ON A2DP SUSPENDED ## p_av_suspend={}", std::format_ptr(p_av_suspend));
   const uint8_t peer_type_sep = btif_av_get_peer_sep(local_a2dp_type);
   if (peer_type_sep == AVDT_TSEP_SRC) {
     btif_a2dp_sink_on_suspended(p_av_suspend);
diff --git a/system/btif/src/btif_av.cc b/system/btif/src/btif_av.cc
index ce8d74f..34d081c 100644
--- a/system/btif/src/btif_av.cc
+++ b/system/btif/src/btif_av.cc
@@ -1553,7 +1553,8 @@
         if ((btif_a2dp_sink_get_audio_track() != nullptr) &&
             (peer->PeerAddress() != peer_address)) {
           log::info("there is another peer with audio track({}), another={}, peer={}",
-                    fmt::ptr(btif_a2dp_sink_get_audio_track()), peer->PeerAddress(), peer_address);
+                    std::format_ptr(btif_a2dp_sink_get_audio_track()), peer->PeerAddress(),
+                    peer_address);
           connected++;
         }
         break;
diff --git a/system/btif/src/btif_core.cc b/system/btif/src/btif_core.cc
index c5a723d..73fe810 100644
--- a/system/btif/src/btif_core.cc
+++ b/system/btif/src/btif_core.cc
@@ -379,7 +379,7 @@
 }
 
 static void btif_core_storage_adapter_write(bt_property_t* prop) {
-  log::verbose("type: {}, len {}, {}", prop->type, prop->len, fmt::ptr(prop->val));
+  log::verbose("type: {}, len {}, {}", prop->type, prop->len, std::format_ptr(prop->val));
   bt_status_t status = btif_storage_set_adapter_property(prop);
   GetInterfaceToProfiles()->events->invoke_adapter_properties_cb(status, 1, prop);
 }
@@ -550,7 +550,7 @@
 
 void btif_set_adapter_property(bt_property_t* property) {
   log::verbose("btif_set_adapter_property type: {}, len {}, {}", property->type, property->len,
-               fmt::ptr(property->val));
+               std::format_ptr(property->val));
 
   switch (property->type) {
     case BT_PROPERTY_BDNAME: {
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 70762b7..f50bd55 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -177,11 +177,11 @@
   ServiceDiscoveryState sdp_over_classic;
 };
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<btif_dm_pairing_cb_t::ServiceDiscoveryState>
     : enum_formatter<btif_dm_pairing_cb_t::ServiceDiscoveryState> {};
-}  // namespace fmt
+}  // namespace std
 
 // TODO(jpawlowski): unify ?
 // btif_dm_local_key_id_t == tBTM_BLE_LOCAL_ID_KEYS == tBTA_BLE_LOCAL_ID_KEYS
diff --git a/system/btif/src/btif_hd.cc b/system/btif/src/btif_hd.cc
index 8b2a673..321e57b 100644
--- a/system/btif/src/btif_hd.cc
+++ b/system/btif/src/btif_hd.cc
@@ -414,11 +414,11 @@
   }
 
   app_info.p_name = (char*)osi_calloc(BTIF_HD_APP_NAME_LEN);
-  strlcpy(app_info.p_name, p_app_param->name, BTIF_HD_APP_NAME_LEN);
+  osi_strlcpy(app_info.p_name, p_app_param->name, BTIF_HD_APP_NAME_LEN);
   app_info.p_description = (char*)osi_calloc(BTIF_HD_APP_DESCRIPTION_LEN);
-  strlcpy(app_info.p_description, p_app_param->description, BTIF_HD_APP_DESCRIPTION_LEN);
+  osi_strlcpy(app_info.p_description, p_app_param->description, BTIF_HD_APP_DESCRIPTION_LEN);
   app_info.p_provider = (char*)osi_calloc(BTIF_HD_APP_PROVIDER_LEN);
-  strlcpy(app_info.p_provider, p_app_param->provider, BTIF_HD_APP_PROVIDER_LEN);
+  osi_strlcpy(app_info.p_provider, p_app_param->provider, BTIF_HD_APP_PROVIDER_LEN);
   app_info.subclass = p_app_param->subclass;
   app_info.descriptor.dl_len = p_app_param->desc_list_len;
   app_info.descriptor.dsc_list = (uint8_t*)osi_malloc(app_info.descriptor.dl_len);
diff --git a/system/btif/src/btif_pan.cc b/system/btif/src/btif_pan.cc
index 1f0bcb1..1b45e9e 100644
--- a/system/btif/src/btif_pan.cc
+++ b/system/btif/src/btif_pan.cc
@@ -294,7 +294,7 @@
 
   // set mac addr
   memset(&ifr, 0, sizeof(ifr));
-  strlcpy(ifr.ifr_name, devname, IFNAMSIZ);
+  osi_strlcpy(ifr.ifr_name, devname, IFNAMSIZ);
   err = ioctl(sk, SIOCGIFHWADDR, &ifr);
   if (err < 0) {
     log::error("Could not get network hardware for interface:{}, errno:{}", devname,
@@ -303,7 +303,7 @@
     return -1;
   }
 
-  strlcpy(ifr.ifr_name, devname, IFNAMSIZ);
+  osi_strlcpy(ifr.ifr_name, devname, IFNAMSIZ);
   memcpy(ifr.ifr_hwaddr.sa_data, addr.address, 6);
 
   /* The IEEE has specified that the most significant bit of the most
@@ -329,7 +329,7 @@
 
   // bring it up
   memset(&ifr, 0, sizeof(ifr));
-  strlcpy(ifr.ifr_name, devname, IF_NAMESIZE);
+  osi_strlcpy(ifr.ifr_name, devname, IF_NAMESIZE);
 
   ifr.ifr_flags |= IFF_UP;
   ifr.ifr_flags |= IFF_MULTICAST;
@@ -356,7 +356,7 @@
   }
 
   memset(&ifr, 0, sizeof(ifr));
-  strlcpy(ifr.ifr_name, devname, IF_NAMESIZE);
+  osi_strlcpy(ifr.ifr_name, devname, IF_NAMESIZE);
 
   ifr.ifr_flags &= ~IFF_UP;
 
@@ -395,7 +395,7 @@
   memset(&ifr, 0, sizeof(ifr));
   ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
 
-  strlcpy(ifr.ifr_name, TAP_IF_NAME, IFNAMSIZ);
+  osi_strlcpy(ifr.ifr_name, TAP_IF_NAME, IFNAMSIZ);
 
   /* try to create the device */
   err = ioctl(fd, TUNSETIFF, (void*)&ifr);
@@ -470,7 +470,7 @@
 static void btpan_open_conn(btpan_conn_t* conn, tBTA_PAN* p_data) {
   log::verbose("btpan_open_conn: local_role:{}, peer_role: {},  handle:{}, conn: {}",
                p_data->open.local_role, p_data->open.peer_role, p_data->open.handle,
-               fmt::ptr(conn));
+               std::format_ptr(conn));
 
   if (conn == NULL) {
     conn = btpan_new_conn(p_data->open.handle, p_data->open.bd_addr, p_data->open.local_role,
@@ -500,7 +500,7 @@
 }
 
 static void btpan_close_conn(btpan_conn_t* conn) {
-  log::verbose("btpan_close_conn: {}", fmt::ptr(conn));
+  log::verbose("btpan_close_conn: {}", std::format_ptr(conn));
 
   if (conn && conn->state == PAN_STATE_OPEN) {
     log::verbose("btpan_close_conn: PAN_STATE_OPEN");
diff --git a/system/btif/src/btif_profile_queue.cc b/system/btif/src/btif_profile_queue.cc
index 38d00f4..3e6e0aa 100644
--- a/system/btif/src/btif_profile_queue.cc
+++ b/system/btif/src/btif_profile_queue.cc
@@ -55,7 +55,7 @@
       : address_(address), uuid_(uuid), busy_(false), connect_cb_(connect_cb) {}
 
   std::string ToString() const {
-    return fmt::format("address={} UUID={:04X} busy={}", address_, uuid_, busy_);
+    return std::format("address={} UUID={:04X} busy={}", address_, uuid_, busy_);
   }
 
   const RawAddress& address() const { return address_; }
diff --git a/system/btif/src/btif_sdp_server.cc b/system/btif/src/btif_sdp_server.cc
index c7c2918..6d3dfd4 100644
--- a/system/btif/src/btif_sdp_server.cc
+++ b/system/btif/src/btif_sdp_server.cc
@@ -72,10 +72,10 @@
   bluetooth_sdp_record* record_data;
 } sdp_slot_t;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<sdp_state_t> : enum_formatter<sdp_state_t> {};
-}  // namespace fmt
+}  // namespace std
 
 #define MAX_SDP_SLOTS 128
 static sdp_slot_t sdp_slots[MAX_SDP_SLOTS];
diff --git a/system/btif/src/btif_sock_rfc.cc b/system/btif/src/btif_sock_rfc.cc
index 3b50ca5..e8bf6b4 100644
--- a/system/btif/src/btif_sock_rfc.cc
+++ b/system/btif/src/btif_sock_rfc.cc
@@ -235,7 +235,7 @@
   slot->service_uuid = uuid;
 
   if (name && *name) {
-    strlcpy(slot->service_name, name, sizeof(slot->service_name));
+    osi_strlcpy(slot->service_name, name, sizeof(slot->service_name));
   } else {
     memset(slot->service_name, 0, sizeof(slot->service_name));
   }
@@ -742,7 +742,8 @@
         break;
       }
       if (p_data->scn == 0) {
-        log::error("Unable to allocate scn: all resources exhausted. slot found: {}", fmt::ptr(rs));
+        log::error("Unable to allocate scn: all resources exhausted. slot found: {}",
+                   std::format_ptr(rs));
         cleanup_rfc_slot(rs);
         break;
       }
@@ -789,7 +790,7 @@
       }
 
       if (!create_server_sdp_record(slot)) {
-        log::error("cannot start server, slot found: {}", fmt::ptr(slot));
+        log::error("cannot start server, slot found: {}", std::format_ptr(slot));
         cleanup_rfc_slot(slot);
         break;
       }
diff --git a/system/common/message_loop_thread.h b/system/common/message_loop_thread.h
index 3c842a0..301a484 100644
--- a/system/common/message_loop_thread.h
+++ b/system/common/message_loop_thread.h
@@ -217,7 +217,7 @@
 }  // namespace common
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::common::MessageLoopThread> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/common/metrics.cc b/system/common/metrics.cc
index 90539a7..49b73a0 100644
--- a/system/common/metrics.cc
+++ b/system/common/metrics.cc
@@ -42,7 +42,7 @@
 #include "osi/include/osi.h"
 #include "types/raw_address.h"
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<android::bluetooth::DirectionEnum>
     : enum_formatter<android::bluetooth::DirectionEnum> {};
@@ -58,7 +58,7 @@
 template <>
 struct formatter<android::bluetooth::DeviceInfoSrcEnum>
     : enum_formatter<android::bluetooth::DeviceInfoSrcEnum> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace bluetooth {
 namespace common {
diff --git a/system/device/Android.bp b/system/device/Android.bp
index 1a5d72a..5411561 100644
--- a/system/device/Android.bp
+++ b/system/device/Android.bp
@@ -109,6 +109,7 @@
         "libchrome",
         "libflagtest",
         "libgmock",
+        "libosi",
         "server_configurable_flags",
     ],
     header_libs: ["libbluetooth_headers"],
diff --git a/system/device/src/device_iot_config.cc b/system/device/src/device_iot_config.cc
index 2da612a..4513e68 100644
--- a/system/device/src/device_iot_config.cc
+++ b/system/device/src/device_iot_config.cc
@@ -219,7 +219,7 @@
     return false;
   }
 
-  strlcpy(value, stored_value->c_str(), *size_bytes);
+  osi_strlcpy(value, stored_value->c_str(), *size_bytes);
   *size_bytes = strlen(value) + 1;
 
   return true;
diff --git a/system/device/src/interop.cc b/system/device/src/interop.cc
index 344198f..1c8798e 100644
--- a/system/device/src/interop.cc
+++ b/system/device/src/interop.cc
@@ -181,10 +181,10 @@
   } entry_type;
 } interop_db_entry_t;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<interop_bl_type> : enum_formatter<interop_bl_type> {};
-}  // namespace fmt
+}  // namespace std
 
 static const char* interop_feature_string_(const interop_feature_t feature);
 static void interop_free_entry_(void* data);
@@ -264,7 +264,7 @@
 
 void interop_database_clear() {
   log::debug("interop_is_initialized: {} interop_list: {}", interop_is_initialized,
-             fmt::ptr(interop_list));
+             std::format_ptr(interop_list));
 
   if (interop_is_initialized && interop_list) {
     for (int feature = BEGINNING_OF_INTEROP_LIST; feature != END_OF_INTEROP_LIST; feature++) {
@@ -825,7 +825,7 @@
 
   if ((token = strtok_r(str, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
     trim(token);
-    strlcpy(bdaddrstr, token, KEY_MAX_LENGTH);
+    osi_strlcpy(bdaddrstr, token, KEY_MAX_LENGTH);
   } else {
     return false;
   }
@@ -845,7 +845,7 @@
 
   if ((token = strtok_r(str, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
     trim(token);
-    strlcpy(addr_start_str, token, 18);
+    osi_strlcpy(addr_start_str, token, 18);
     if (!RawAddress::FromString(addr_start_str, *addr_start)) {
       return false;
     }
@@ -855,7 +855,7 @@
 
   if ((token = strtok_r(NULL, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
     trim(token);
-    strlcpy(addr_end_str, token, 18);
+    osi_strlcpy(addr_end_str, token, 18);
     if (RawAddress::FromString(addr_end_str, *addr_end)) {
       ret_value = true;
     }
@@ -870,7 +870,7 @@
 
   if ((token = strtok_r(str, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
     trim(token);
-    strlcpy(bdaddrstr, token, KEY_MAX_LENGTH);
+    osi_strlcpy(bdaddrstr, token, KEY_MAX_LENGTH);
   } else {
     return false;
   }
@@ -931,7 +931,7 @@
     interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
     entry->bl_type = INTEROP_BL_TYPE_NAME;
     entry->bl_entry_type = entry_type;
-    strlcpy(entry->entry_type.name_entry.name, key, sizeof(entry->entry_type.name_entry.name));
+    osi_strlcpy(entry->entry_type.name_entry.name, key, sizeof(entry->entry_type.name_entry.name));
     entry->entry_type.name_entry.feature = (interop_feature_t)feature;
     entry->entry_type.name_entry.length = strlen(key);
     interop_database_add_(entry, false);
@@ -965,7 +965,7 @@
       return false;
     }
 
-    strlcpy(tmp_key, key, VALID_VNDR_PRDT_LEN + 1);
+    osi_strlcpy(tmp_key, key, VALID_VNDR_PRDT_LEN + 1);
     if (!get_vendor_product_id(tmp_key, &vendor_id, &product_id)) {
       log::warn("Error in parsing vendor/product id {}", key);
       return false;
@@ -988,7 +988,7 @@
       return false;
     }
 
-    strlcpy(tmp_key, key, KEY_MAX_LENGTH);
+    osi_strlcpy(tmp_key, key, KEY_MAX_LENGTH);
     if (!get_addr_maxlat(tmp_key, bdaddr_str, &max_lat)) {
       log::warn("Error in parsing address and max_lat {}", key);
       return false;
@@ -1049,7 +1049,7 @@
       return false;
     }
 
-    strlcpy(tmp_key, key, KEY_MAX_LENGTH);
+    osi_strlcpy(tmp_key, key, KEY_MAX_LENGTH);
     if (!get_addr_lmp_ver(tmp_key, bdaddr_str, &lmp_ver, &lmp_sub_ver)) {
       log::warn("Error in parsing address and lmp_ver {}", key);
       return false;
@@ -1092,7 +1092,7 @@
       return false;
     }
 
-    strlcpy(tmp_key, key, VALID_ADDR_RANGE_LEN + 1);
+    osi_strlcpy(tmp_key, key, VALID_ADDR_RANGE_LEN + 1);
     if (!get_addr_range(tmp_key, &addr_start, &addr_end)) {
       log::warn("key: {} addr_start {} or addr end  {} is added to interop list", key, addr_start,
                 addr_end);
@@ -1178,7 +1178,7 @@
   interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
   entry->bl_type = INTEROP_BL_TYPE_NAME;
   entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
-  strlcpy(entry->entry_type.name_entry.name, name, sizeof(entry->entry_type.name_entry.name));
+  osi_strlcpy(entry->entry_type.name_entry.name, name, sizeof(entry->entry_type.name_entry.name));
   entry->entry_type.name_entry.feature = (interop_feature_t)feature;
   entry->entry_type.name_entry.length = name_length;
   interop_database_add_(entry, true);
@@ -1262,11 +1262,11 @@
   char trim_name[KEY_MAX_LENGTH] = {'\0'};
   log::assert_that(name != nullptr, "assert failed: name != nullptr");
 
-  strlcpy(trim_name, name, KEY_MAX_LENGTH);
+  osi_strlcpy(trim_name, name, KEY_MAX_LENGTH);
   interop_db_entry_t entry;
 
   entry.bl_type = INTEROP_BL_TYPE_NAME;
-  strlcpy(entry.entry_type.name_entry.name, trim(trim_name), KEY_MAX_LENGTH);
+  osi_strlcpy(entry.entry_type.name_entry.name, trim(trim_name), KEY_MAX_LENGTH);
   entry.entry_type.name_entry.feature = (interop_feature_t)feature;
   entry.entry_type.name_entry.length = strlen(entry.entry_type.name_entry.name);
 
@@ -1404,7 +1404,7 @@
 
   entry.bl_type = INTEROP_BL_TYPE_NAME;
   entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
-  strlcpy(entry.entry_type.name_entry.name, name, 20);
+  osi_strlcpy(entry.entry_type.name_entry.name, name, 20);
   entry.entry_type.name_entry.feature = (interop_feature_t)feature;
   entry.entry_type.name_entry.length = strlen(entry.entry_type.name_entry.name);
   if (interop_database_remove_(&entry)) {
diff --git a/system/gd/hal/snoop_logger.cc b/system/gd/hal/snoop_logger.cc
index 0fc1939..d08ad0b 100644
--- a/system/gd/hal/snoop_logger.cc
+++ b/system/gd/hal/snoop_logger.cc
@@ -1409,12 +1409,12 @@
           evt_code == static_cast<uint8_t>(hci::EventCode::VENDOR_SPECIFIC)) {
         uint8_t subevt_code = packet[2];
         std::string message =
-                fmt::format("BTSL:{}/{}/{}/{:02x}/{:02x}", static_cast<uint8_t>(type),
+                std::format("BTSL:{}/{}/{}/{:02x}/{:02x}", static_cast<uint8_t>(type),
                             static_cast<uint8_t>(direction), packet.size(), evt_code, subevt_code);
 
         ATRACE_INSTANT_FOR_TRACK(LOG_TAG, message.c_str());
       } else {
-        std::string message = fmt::format("BTSL:{}/{}/{}/{:02x}", static_cast<uint8_t>(type),
+        std::string message = std::format("BTSL:{}/{}/{}/{:02x}", static_cast<uint8_t>(type),
                                           static_cast<uint8_t>(direction), packet.size(), evt_code);
 
         ATRACE_INSTANT_FOR_TRACK(LOG_TAG, message.c_str());
@@ -1423,7 +1423,7 @@
     case PacketType::CMD: {
       uint16_t op_code = packet[0] | (packet[1] << 8);
 
-      std::string message = fmt::format("BTSL:{}/{}/{}/{:04x}", static_cast<uint8_t>(type),
+      std::string message = std::format("BTSL:{}/{}/{}/{:04x}", static_cast<uint8_t>(type),
                                         static_cast<uint8_t>(direction), packet.size(), op_code);
 
       ATRACE_INSTANT_FOR_TRACK(LOG_TAG, message.c_str());
@@ -1432,15 +1432,15 @@
       uint16_t handle = (packet[0] | (packet[1] << 8)) & 0x0fff;
       uint8_t pb_flag = (packet[1] & 0x30) >> 4;
 
-      std::string message = fmt::format("BTSL:{}/{}/{}/{:03x}/{}", static_cast<uint8_t>(type),
-                                        static_cast<uint8_t>(direction), packet.size(), handle,
-                                        pb_flag);
+      std::string message =
+              std::format("BTSL:{}/{}/{}/{:03x}/{}", static_cast<uint8_t>(type),
+                          static_cast<uint8_t>(direction), packet.size(), handle, pb_flag);
 
       ATRACE_INSTANT_FOR_TRACK(LOG_TAG, message.c_str());
     } break;
     case PacketType::ISO:
     case PacketType::SCO: {
-      std::string message = fmt::format("BTSL:{}/{}/{}", static_cast<uint8_t>(type),
+      std::string message = std::format("BTSL:{}/{}/{}", static_cast<uint8_t>(type),
                                         static_cast<uint8_t>(direction), packet.size());
 
       ATRACE_INSTANT_FOR_TRACK(LOG_TAG, message.c_str());
diff --git a/system/gd/hal/snoop_logger.h b/system/gd/hal/snoop_logger.h
index bcd406c..f3a4add 100644
--- a/system/gd/hal/snoop_logger.h
+++ b/system/gd/hal/snoop_logger.h
@@ -343,8 +343,8 @@
 }  // namespace hal
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::hal::profile_type_t> : enum_formatter<bluetooth::hal::profile_type_t> {
 };
-}  // namespace fmt
+}  // namespace std
diff --git a/system/gd/hci/acl_manager/round_robin_scheduler.h b/system/gd/hci/acl_manager/round_robin_scheduler.h
index 8d51ccd..61be5f1 100644
--- a/system/gd/hci/acl_manager/round_robin_scheduler.h
+++ b/system/gd/hci/acl_manager/round_robin_scheduler.h
@@ -89,8 +89,8 @@
 }  // namespace hci
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::hci::acl_manager::RoundRobinScheduler::ConnectionType>
     : enum_formatter<bluetooth::hci::acl_manager::RoundRobinScheduler::ConnectionType> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/gd/hci/address.h b/system/gd/hci/address.h
index d3626fd..b7e2bde 100644
--- a/system/gd/hci/address.h
+++ b/system/gd/hci/address.h
@@ -114,15 +114,15 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::hci::Address> : formatter<std::string> {
   template <class Context>
   typename Context::iterator format(const bluetooth::hci::Address& address, Context& ctx) const {
     std::string repr = address.ToRedactedStringForLogging();
-    return fmt::formatter<std::string>::format(repr, ctx);
+    return std::formatter<std::string>::format(repr, ctx);
   }
 };
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>
diff --git a/system/gd/hci/address_with_type.h b/system/gd/hci/address_with_type.h
index 0c3eb5e..c196989 100644
--- a/system/gd/hci/address_with_type.h
+++ b/system/gd/hci/address_with_type.h
@@ -155,16 +155,16 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::hci::AddressWithType> : formatter<std::string> {
   template <class Context>
   typename Context::iterator format(const bluetooth::hci::AddressWithType& address,
                                     Context& ctx) const {
     std::string repr = address.ToRedactedStringForLogging();
-    return fmt::formatter<std::string>::format(repr, ctx);
+    return std::formatter<std::string>::format(repr, ctx);
   }
 };
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>
diff --git a/system/gd/hci/controller.cc b/system/gd/hci/controller.cc
index 1f88332..38415f5 100644
--- a/system/gd/hci/controller.cc
+++ b/system/gd/hci/controller.cc
@@ -1560,9 +1560,9 @@
 
 template <typename OutputT>
 void Controller::impl::dump(OutputT&& out) const {
-  fmt::format_to(out, "\nHCI Controller Dumpsys:\n");
+  std::format_to(out, "\nHCI Controller Dumpsys:\n");
 
-  fmt::format_to(out,
+  std::format_to(out,
                  "    local_version_information:\n"
                  "        hci_version: {}\n"
                  "        hci_revision: 0x{:x}\n"
@@ -1575,7 +1575,7 @@
                  local_version_information_.lmp_subversion_,
                  local_version_information_.manufacturer_name_);
 
-  fmt::format_to(out,
+  std::format_to(out,
                  "    buffer_size:\n"
                  "        acl_data_packet_length: {}\n"
                  "        total_num_acl_data_packets: {}\n"
@@ -1583,7 +1583,7 @@
                  "        total_num_sco_data_packets: {}\n",
                  acl_buffer_length_, acl_buffers_, sco_buffer_length_, sco_buffers_);
 
-  fmt::format_to(out,
+  std::format_to(out,
                  "    le_buffer_size:\n"
                  "        le_acl_data_packet_length: {}\n"
                  "        total_num_le_acl_data_packets: {}\n"
@@ -1592,7 +1592,7 @@
                  le_buffer_size_.le_data_packet_length_, le_buffer_size_.total_num_le_packets_,
                  iso_buffer_size_.le_data_packet_length_, iso_buffer_size_.total_num_le_packets_);
 
-  fmt::format_to(out,
+  std::format_to(out,
                  "    le_maximum_data_length:\n"
                  "        supported_max_tx_octets: {}\n"
                  "        supported_max_tx_time: {}\n"
@@ -1603,7 +1603,7 @@
                  le_maximum_data_length_.supported_max_rx_octets_,
                  le_maximum_data_length_.supported_max_rx_time_);
 
-  fmt::format_to(out,
+  std::format_to(out,
                  "    le_accept_list_size: {}\n"
                  "    le_resolving_list_size: {}\n"
                  "    le_maximum_advertising_data_length: {}\n"
@@ -1615,7 +1615,7 @@
                  le_suggested_default_data_length_, le_number_supported_advertising_sets_,
                  le_periodic_advertiser_list_size_, le_supported_states_);
 
-  fmt::format_to(out,
+  std::format_to(out,
                  "    local_supported_features:\n"
                  "        page0: 0x{:016x}\n"
                  "        page1: 0x{:016x}\n"
@@ -1625,16 +1625,16 @@
                  extended_lmp_features_array_[0], extended_lmp_features_array_[1],
                  extended_lmp_features_array_[2], le_local_supported_features_);
 
-  fmt::format_to(out, "    local_supported_commands: [");
+  std::format_to(out, "    local_supported_commands: [");
   for (size_t i = 0; i < local_supported_commands_.size(); i++) {
     if ((i % 8) == 0) {
-      fmt::format_to(out, "\n       ");
+      std::format_to(out, "\n       ");
     }
-    fmt::format_to(out, " 0x{:02x},", local_supported_commands_[i]);
+    std::format_to(out, " 0x{:02x},", local_supported_commands_[i]);
   }
-  fmt::format_to(out, "\n    ]\n");
+  std::format_to(out, "\n    ]\n");
 
-  fmt::format_to(
+  std::format_to(
           out,
           "    vendor_capabilities:\n"
           "        is_supported: {}\n"
diff --git a/system/gd/hci/distance_measurement_manager.h b/system/gd/hci/distance_measurement_manager.h
index 70739c3..93f0f5a 100644
--- a/system/gd/hci/distance_measurement_manager.h
+++ b/system/gd/hci/distance_measurement_manager.h
@@ -119,8 +119,8 @@
 }  // namespace hci
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::hci::DistanceMeasurementMethod>
     : enum_formatter<bluetooth::hci::DistanceMeasurementMethod> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/gd/hci/facade/le_acl_manager_facade.cc b/system/gd/hci/facade/le_acl_manager_facade.cc
index e80cad4..3e7ff96 100644
--- a/system/gd/hci/facade/le_acl_manager_facade.cc
+++ b/system/gd/hci/facade/le_acl_manager_facade.cc
@@ -36,11 +36,11 @@
 
 using ::bluetooth::packet::RawBuilder;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<blueberry::facade::BluetoothAddressTypeEnum>
     : enum_formatter<blueberry::facade::BluetoothAddressTypeEnum> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace bluetooth {
 namespace hci {
diff --git a/system/gd/hci/le_advertising_manager.h b/system/gd/hci/le_advertising_manager.h
index 52d0802..aa3fbbd 100644
--- a/system/gd/hci/le_advertising_manager.h
+++ b/system/gd/hci/le_advertising_manager.h
@@ -185,8 +185,8 @@
 }  // namespace hci
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::hci::AdvertiserAddressType>
     : enum_formatter<bluetooth::hci::AdvertiserAddressType> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/gd/os/android/metrics.cc b/system/gd/os/android/metrics.cc
index 6956365..d4185ef 100644
--- a/system/gd/os/android/metrics.cc
+++ b/system/gd/os/android/metrics.cc
@@ -31,7 +31,7 @@
 #include "hardware/bt_av.h"
 #include "hci/hci_packets.h"
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<android::bluetooth::DirectionEnum>
     : enum_formatter<android::bluetooth::DirectionEnum> {};
@@ -51,7 +51,7 @@
 struct formatter<android::bluetooth::EventType> : enum_formatter<android::bluetooth::EventType> {};
 template <>
 struct formatter<android::bluetooth::State> : enum_formatter<android::bluetooth::State> {};
-}  // namespace fmt
+}  // namespace std
 
 namespace bluetooth {
 namespace os {
diff --git a/system/gd/os/internal/wakelock_native.h b/system/gd/os/internal/wakelock_native.h
index 82c7069..f1eedbc 100644
--- a/system/gd/os/internal/wakelock_native.h
+++ b/system/gd/os/internal/wakelock_native.h
@@ -52,8 +52,8 @@
 }  // namespace os
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::os::internal::WakelockNative::StatusCode>
     : enum_formatter<bluetooth::os::internal::WakelockNative::StatusCode> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/gd/os/linux_generic/alarm.cc b/system/gd/os/linux_generic/alarm.cc
index 46c06e5..b4de209 100644
--- a/system/gd/os/linux_generic/alarm.cc
+++ b/system/gd/os/linux_generic/alarm.cc
@@ -86,9 +86,9 @@
   lock.unlock();
 
   if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation() && bytes_read == -1) {
-    log::info("No data to read.");
+    log::debug("No data to read.");
     if (errno == EAGAIN || errno == EWOULDBLOCK) {
-      log::info("Alarm is already canceled or rescheduled.");
+      log::debug("Alarm is already canceled or rescheduled.");
       return;
     }
   }
diff --git a/system/gd/packet/parser/gen_cpp.cc b/system/gd/packet/parser/gen_cpp.cc
index 199624f..7dd2dee 100644
--- a/system/gd/packet/parser/gen_cpp.cc
+++ b/system/gd/packet/parser/gen_cpp.cc
@@ -271,7 +271,7 @@
     namespace_prefix += "::";
   }
 
-  out_file << "#if __has_include(<bluetooth/log.h>)" << std::endl << "namespace fmt {" << std::endl;
+  out_file << "#if __has_include(<bluetooth/log.h>)" << std::endl << "namespace std {" << std::endl;
   for (const auto& e : decls.type_defs_queue_) {
     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
       const auto* enum_def = static_cast<const EnumDef*>(e.second);
@@ -281,7 +281,7 @@
                << std::endl;
     }
   }
-  out_file << "} // namespace fmt" << std::endl
+  out_file << "} // namespace std" << std::endl
            << "#endif // __has_include(<bluetooth/log.h>)" << std::endl;
 
   out_file.close();
diff --git a/system/include/hardware/avrcp/avrcp_logging_helper.h b/system/include/hardware/avrcp/avrcp_logging_helper.h
index 5ae1f40..c53d2ec 100644
--- a/system/include/hardware/avrcp/avrcp_logging_helper.h
+++ b/system/include/hardware/avrcp/avrcp_logging_helper.h
@@ -41,7 +41,7 @@
     CASE_RETURN_TEXT(CType::CHANGED);
     CASE_RETURN_TEXT(CType::INTERIM);
     default:
-      return fmt::format("Unknown CType: 0x{:x}", (uint8_t)type);
+      return std::format("Unknown CType: 0x{:x}", (uint8_t)type);
   }
 }
 
@@ -56,7 +56,7 @@
     CASE_RETURN_TEXT(Opcode::SUBUNIT_INFO);
     CASE_RETURN_TEXT(Opcode::PASS_THROUGH);
     default:
-      return fmt::format("Unknown Opcode: 0x{:x}", (uint8_t)opcode);
+      return std::format("Unknown Opcode: 0x{:x}", (uint8_t)opcode);
   }
 }
 
@@ -78,7 +78,7 @@
     CASE_RETURN_TEXT(CommandPdu::SET_ADDRESSED_PLAYER);
     CASE_RETURN_TEXT(CommandPdu::PLAY_ITEM);
     default:
-      return fmt::format("Unknown Command PDU: 0x{:x}", (uint8_t)pdu);
+      return std::format("Unknown Command PDU: 0x{:x}", (uint8_t)pdu);
   }
 }
 
@@ -90,7 +90,7 @@
   switch (type) {
     CASE_RETURN_TEXT(PacketType::SINGLE);
     default:
-      return fmt::format("Unknown Packet Type: 0x{:x}", (uint8_t)type);
+      return std::format("Unknown Packet Type: 0x{:x}", (uint8_t)type);
   }
 }
 
@@ -103,7 +103,7 @@
     CASE_RETURN_TEXT(Capability::COMPANY_ID);
     CASE_RETURN_TEXT(Capability::EVENTS_SUPPORTED);
     default:
-      return fmt::format("Unknown Capability: 0x{:x}", (uint8_t)cap);
+      return std::format("Unknown Capability: 0x{:x}", (uint8_t)cap);
   }
 }
 
@@ -123,7 +123,7 @@
     CASE_RETURN_TEXT(Event::UIDS_CHANGED);
     CASE_RETURN_TEXT(Event::VOLUME_CHANGED);
     default:
-      return fmt::format("Unknown Event: 0x{:x}", (uint8_t)event);
+      return std::format("Unknown Event: 0x{:x}", (uint8_t)event);
   }
 }
 
@@ -142,7 +142,7 @@
     CASE_RETURN_TEXT(Attribute::PLAYING_TIME);
     CASE_RETURN_TEXT(Attribute::DEFAULT_COVER_ART);
     default:
-      return fmt::format("Unknown Attribute Value: 0x{:x}", (uint32_t)attr);
+      return std::format("Unknown Attribute Value: 0x{:x}", (uint32_t)attr);
   }
 }
 
@@ -176,7 +176,7 @@
     CASE_RETURN_TEXT(Status::NO_AVAILABLE_PLAYERS);
     CASE_RETURN_TEXT(Status::ADDRESSED_PLAYER_CHANGED);
     default:
-      return fmt::format("Unknown Status: 0x{:x}", (uint8_t)status);
+      return std::format("Unknown Status: 0x{:x}", (uint8_t)status);
   }
 }
 
@@ -191,7 +191,7 @@
     CASE_RETURN_TEXT(BrowsePdu::CHANGE_PATH);
     CASE_RETURN_TEXT(BrowsePdu::GET_ITEM_ATTRIBUTES);
     default:
-      return fmt::format("Unknown Browse PDU: 0x{:x}", (uint8_t)pdu);
+      return std::format("Unknown Browse PDU: 0x{:x}", (uint8_t)pdu);
   }
 }
 
@@ -206,7 +206,7 @@
     CASE_RETURN_TEXT(Scope::SEARCH);
     CASE_RETURN_TEXT(Scope::NOW_PLAYING);
     default:
-      return fmt::format("Unknown Scope: 0x{:x}", (uint8_t)scope);
+      return std::format("Unknown Scope: 0x{:x}", (uint8_t)scope);
   }
 }
 
@@ -217,7 +217,7 @@
     CASE_RETURN_TEXT(Direction::UP);
     CASE_RETURN_TEXT(Direction::DOWN);
     default:
-      return fmt::format("Unknown Direction: 0x{:x}", (uint8_t)dir);
+      return std::format("Unknown Direction: 0x{:x}", (uint8_t)dir);
   }
 }
 
@@ -230,7 +230,7 @@
     CASE_RETURN_TEXT(KeyState::PUSHED);
     CASE_RETURN_TEXT(KeyState::RELEASED);
     default:
-      return fmt::format("Unknown KeyState: 0x{:x}", (uint8_t)state);
+      return std::format("Unknown KeyState: 0x{:x}", (uint8_t)state);
   }
 }
 
@@ -245,7 +245,7 @@
     CASE_RETURN_TEXT(PlayerAttribute::SHUFFLE);
     CASE_RETURN_TEXT(PlayerAttribute::SCAN);
   }
-  return fmt::format("Unknown Player Attribute: 0x{:x}", (uint8_t)attr);
+  return std::format("Unknown Player Attribute: 0x{:x}", (uint8_t)attr);
 }
 
 inline std::ostream& operator<<(std::ostream& os, const PlayerAttribute& attr) {
@@ -259,7 +259,7 @@
     CASE_RETURN_TEXT(PlayerRepeatValue::ALL);
     CASE_RETURN_TEXT(PlayerRepeatValue::GROUP);
   }
-  return fmt::format("Unknown Player Repeat Value: 0x{:x}", (uint8_t)val);
+  return std::format("Unknown Player Repeat Value: 0x{:x}", (uint8_t)val);
 }
 
 inline std::ostream& operator<<(std::ostream& os, const PlayerRepeatValue& val) {
@@ -272,7 +272,7 @@
     CASE_RETURN_TEXT(PlayerShuffleValue::ALL);
     CASE_RETURN_TEXT(PlayerShuffleValue::GROUP);
   }
-  return fmt::format("Unknown Player Shuffle Value: 0x{:x}", (uint8_t)val);
+  return std::format("Unknown Player Shuffle Value: 0x{:x}", (uint8_t)val);
 }
 
 inline std::ostream& operator<<(std::ostream& os, const PlayerShuffleValue& val) {
@@ -282,7 +282,7 @@
 }  // namespace avrcp
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::avrcp::CType> : ostream_formatter {};
 template <>
@@ -313,4 +313,4 @@
 struct formatter<bluetooth::avrcp::PlayerRepeatValue> : ostream_formatter {};
 template <>
 struct formatter<bluetooth::avrcp::PlayerShuffleValue> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index 5ae5549..5593e6b 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -1000,7 +1000,7 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bt_status_t> : enum_formatter<bt_status_t> {};
 template <>
@@ -1009,7 +1009,7 @@
 struct formatter<bt_bond_state_t> : enum_formatter<bt_bond_state_t> {};
 template <>
 struct formatter<bt_property_type_t> : enum_formatter<bt_property_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
 
diff --git a/system/include/hardware/bt_av.h b/system/include/hardware/bt_av.h
index c6a9d7a..b839cb0 100644
--- a/system/include/hardware/bt_av.h
+++ b/system/include/hardware/bt_av.h
@@ -21,6 +21,7 @@
 #include <hardware/bluetooth.h>
 
 #include <optional>
+#include <sstream>
 #include <vector>
 
 #include "types/raw_address.h"
@@ -286,7 +287,7 @@
 
 __END_DECLS
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<btav_connection_state_t> : enum_formatter<btav_connection_state_t> {};
 template <>
@@ -306,6 +307,6 @@
 template <>
 struct formatter<btav_a2dp_scmst_enable_status_t>
     : enum_formatter<btav_a2dp_scmst_enable_status_t> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* ANDROID_INCLUDE_BT_AV_H */
diff --git a/system/include/hardware/bt_common_types.h b/system/include/hardware/bt_common_types.h
index e3d6b39..d7b6cc4 100644
--- a/system/include/hardware/bt_common_types.h
+++ b/system/include/hardware/bt_common_types.h
@@ -150,10 +150,10 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bt_gatt_db_attribute_type_t> : enum_formatter<bt_gatt_db_attribute_type_t> {};
-}  // namespace fmt
+}  // namespace std
 #endif  // __has_include(<bluetooth/log.h>)
 
 #endif /* ANDROID_INCLUDE_BT_COMMON_TYPES_H */
diff --git a/system/include/hardware/bt_gmap.h b/system/include/hardware/bt_gmap.h
index bc3e669..7d722fb 100644
--- a/system/include/hardware/bt_gmap.h
+++ b/system/include/hardware/bt_gmap.h
@@ -44,7 +44,7 @@
 }  // namespace gmap
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::gmap::RolesBitMask> : enum_formatter<bluetooth::gmap::RolesBitMask> {};
 template <>
@@ -53,4 +53,4 @@
 template <>
 struct formatter<bluetooth::gmap::UGGFeatureBitMask>
     : enum_formatter<bluetooth::gmap::UGGFeatureBitMask> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/include/hardware/bt_hd.h b/system/include/hardware/bt_hd.h
index f86e2ee..0a4c78f 100644
--- a/system/include/hardware/bt_hd.h
+++ b/system/include/hardware/bt_hd.h
@@ -120,10 +120,10 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bthd_report_type_t> : enum_formatter<bthd_report_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
 
diff --git a/system/include/hardware/bt_hf.h b/system/include/hardware/bt_hf.h
index 48771e2..48ff4d1 100644
--- a/system/include/hardware/bt_hf.h
+++ b/system/include/hardware/bt_hf.h
@@ -118,7 +118,7 @@
 }  // namespace headset
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::headset::bthf_connection_state_t>
     : enum_formatter<bluetooth::headset::bthf_connection_state_t> {};
@@ -149,4 +149,4 @@
 template <>
 struct formatter<bluetooth::headset::bthf_swb_config_t>
     : enum_formatter<bluetooth::headset::bthf_swb_config_t> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/include/hardware/bt_hf_client.h b/system/include/hardware/bt_hf_client.h
index fe4c98b..24a1c10 100644
--- a/system/include/hardware/bt_hf_client.h
+++ b/system/include/hardware/bt_hf_client.h
@@ -384,8 +384,8 @@
   bt_status_t (*send_android_at)(const RawAddress* bd_addr, const char* arg);
 } bthf_client_interface_t;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bthf_client_connection_state_t> : enum_formatter<bthf_client_connection_state_t> {
 };
-}  // namespace fmt
+}  // namespace std
diff --git a/system/include/hardware/bt_hh.h b/system/include/hardware/bt_hh.h
index b326fc1..38f41ff 100644
--- a/system/include/hardware/bt_hh.h
+++ b/system/include/hardware/bt_hh.h
@@ -232,14 +232,14 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bthh_connection_state_t> : enum_formatter<bthh_connection_state_t> {};
 template <>
 struct formatter<bthh_protocol_mode_t> : enum_formatter<bthh_protocol_mode_t> {};
 template <>
 struct formatter<bthh_report_type_t> : enum_formatter<bthh_report_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
 
diff --git a/system/include/hardware/bt_le_audio.h b/system/include/hardware/bt_le_audio.h
index 8feb83f..91ce17c 100644
--- a/system/include/hardware/bt_le_audio.h
+++ b/system/include/hardware/bt_le_audio.h
@@ -547,7 +547,7 @@
 } /* namespace le_audio */
 } /* namespace bluetooth */
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::le_audio::btle_audio_codec_index_t>
     : enum_formatter<bluetooth::le_audio::btle_audio_codec_index_t> {};
@@ -566,4 +566,4 @@
 template <>
 struct formatter<bluetooth::le_audio::GroupStreamStatus>
     : enum_formatter<bluetooth::le_audio::GroupStreamStatus> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/include/hardware/bt_pan.h b/system/include/hardware/bt_pan.h
index 95ce4ac..bf8d6151 100644
--- a/system/include/hardware/bt_pan.h
+++ b/system/include/hardware/bt_pan.h
@@ -83,10 +83,10 @@
   void (*cleanup)(void);
 } btpan_interface_t;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<btpan_connection_state_t> : enum_formatter<btpan_connection_state_t> {};
 
 template <>
 struct formatter<btpan_control_state_t> : enum_formatter<btpan_control_state_t> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/include/hardware/bt_rc.h b/system/include/hardware/bt_rc.h
index d6a984e..7438c93 100644
--- a/system/include/hardware/bt_rc.h
+++ b/system/include/hardware/bt_rc.h
@@ -680,7 +680,7 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<btrc_status_t> : enum_formatter<btrc_status_t> {};
 template <>
@@ -689,7 +689,7 @@
 struct formatter<btrc_remote_features_t> : enum_formatter<btrc_remote_features_t> {};
 template <>
 struct formatter<btrc_notification_type_t> : enum_formatter<btrc_notification_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
 
diff --git a/system/include/hardware/bt_sdp.h b/system/include/hardware/bt_sdp.h
index 8246bc7..d595637 100644
--- a/system/include/hardware/bt_sdp.h
+++ b/system/include/hardware/bt_sdp.h
@@ -178,9 +178,9 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth_sdp_types> : enum_formatter<bluetooth_sdp_types> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
diff --git a/system/include/hardware/bt_sock.h b/system/include/hardware/bt_sock.h
index fd28ff3..ad8cc45 100644
--- a/system/include/hardware/bt_sock.h
+++ b/system/include/hardware/bt_sock.h
@@ -125,9 +125,9 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<btsock_type_t> : enum_formatter<btsock_type_t> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
diff --git a/system/include/hardware/bt_vc.h b/system/include/hardware/bt_vc.h
index 10ede4f..50adebe 100644
--- a/system/include/hardware/bt_vc.h
+++ b/system/include/hardware/bt_vc.h
@@ -150,11 +150,11 @@
 } /* namespace vc */
 } /* namespace bluetooth */
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::vc::VolumeInputType> : enum_formatter<bluetooth::vc::VolumeInputType> {
 };
 template <>
 struct formatter<bluetooth::vc::VolumeInputStatus>
     : enum_formatter<bluetooth::vc::VolumeInputStatus> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/include/macros.h b/system/include/macros.h
index 1bf83cd..9fb3891 100644
--- a/system/include/macros.h
+++ b/system/include/macros.h
@@ -27,11 +27,11 @@
 
 #define CASE_RETURN_STRING(enumerator) \
   case enumerator:                     \
-    return fmt::format(#enumerator "(0x{:x})", static_cast<uint64_t>(enumerator))
+    return std::format(#enumerator "(0x{:x})", static_cast<uint64_t>(enumerator))
 
 #define CASE_RETURN_STRING_HEX04(enumerator) \
   case enumerator:                           \
-    return fmt::format(#enumerator "(0x{:04x})", static_cast<uint64_t>(enumerator))
+    return std::format(#enumerator "(0x{:04x})", static_cast<uint64_t>(enumerator))
 
 #define RETURN_UNKNOWN_TYPE_STRING(type, variable) \
-  return fmt::format("Unknown {}(0x{:x})", #type, static_cast<uint64_t>(variable))
+  return std::format("Unknown {}(0x{:x})", #type, static_cast<uint64_t>(variable))
diff --git a/system/log/include/bluetooth/log.h b/system/log/include/bluetooth/log.h
index b8921c6..34cb605 100644
--- a/system/log/include/bluetooth/log.h
+++ b/system/log/include/bluetooth/log.h
@@ -16,9 +16,10 @@
 
 #pragma once
 
-#include <fmt/core.h>
-#include <fmt/ranges.h>
-#include <fmt/std.h>
+#include <atomic>
+#include <format>
+#include <sstream>
+#include <string_view>
 
 #ifndef LOG_TAG
 #define LOG_TAG "bluetooth"
@@ -53,8 +54,8 @@
 
 /// Write a single log line.
 /// The implementation of this function is dependent on the backend.
-void vlog(Level level, char const* tag, source_location location, fmt::string_view fmt,
-          fmt::format_args vargs);
+void vlog(Level level, char const* tag, source_location location, std::string_view fmt,
+          std::format_args vargs);
 
 /// Capture invalid parameter values that would cause runtime
 /// formatting errors.
@@ -85,16 +86,15 @@
 
 template <Level level, typename... T>
 struct log {
-  log(fmt::format_string<T...> fmt, T&&... args, source_location location = source_location()) {
-    vlog(level, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
-         fmt::make_format_args(format_replace(args)...));
+  log(std::format_string<T...> fmt, T&&... args, source_location location = source_location()) {
+    vlog(level, LOG_TAG, location, fmt.get(), std::make_format_args(format_replace(args)...));
   }
 };
 
 #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
 
 template <int level, typename... T>
-log(fmt::format_string<T...>, T&&...) -> log<level, T...>;
+log(std::format_string<T...>, T&&...) -> log<level, T...>;
 
 #endif
 
@@ -139,61 +139,60 @@
 };
 
 template <typename... T>
-error(fmt::format_string<T...>, T&&...) -> error<T...>;
+error(std::format_string<T...>, T&&...) -> error<T...>;
 template <typename... T>
-warn(fmt::format_string<T...>, T&&...) -> warn<T...>;
+warn(std::format_string<T...>, T&&...) -> warn<T...>;
 template <typename... T>
-info(fmt::format_string<T...>, T&&...) -> info<T...>;
+info(std::format_string<T...>, T&&...) -> info<T...>;
 template <typename... T>
-debug(fmt::format_string<T...>, T&&...) -> debug<T...>;
+debug(std::format_string<T...>, T&&...) -> debug<T...>;
 template <typename... T>
-verbose(fmt::format_string<T...>, T&&...) -> verbose<T...>;
+verbose(std::format_string<T...>, T&&...) -> verbose<T...>;
 
 #endif  // GCC / C++20
 
 [[noreturn]] [[maybe_unused]] static void fatal(
-        fmt::format_string<> fmt,
+        std::format_string<> fmt,
         log_internal::source_location location = log_internal::source_location()) {
-  vlog(log_internal::kFatal, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
-       fmt::make_format_args());
+  vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args());
   std::abort();  // Enforce [[noreturn]]
 }
 
 template <typename T0>
 [[noreturn]] [[maybe_unused]] static void fatal(
-        fmt::format_string<T0> fmt, T0&& arg0,
+        std::format_string<T0> fmt, T0&& arg0,
         log_internal::source_location location = log_internal::source_location()) {
-  vlog(log_internal::kFatal, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
-       fmt::make_format_args(log_internal::format_replace(arg0)));
+  vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
+       std::make_format_args(log_internal::format_replace(arg0)));
   std::abort();  // Enforce [[noreturn]]
 }
 
 template <typename T0, typename T1>
 [[noreturn]] [[maybe_unused]] static void fatal(
-        fmt::format_string<T0, T1> fmt, T0&& arg0, T1&& arg1,
+        std::format_string<T0, T1> fmt, T0&& arg0, T1&& arg1,
         log_internal::source_location location = log_internal::source_location()) {
-  vlog(log_internal::kFatal, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
-       fmt::make_format_args(log_internal::format_replace(arg0),
+  vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
+       std::make_format_args(log_internal::format_replace(arg0),
                              log_internal::format_replace(arg1)));
   std::abort();  // Enforce [[noreturn]]
 }
 
 template <typename T0, typename T1, typename T2>
 [[noreturn]] [[maybe_unused]] static void fatal(
-        fmt::format_string<T0, T1, T2> fmt, T0&& arg0, T1&& arg1, T2&& arg2,
+        std::format_string<T0, T1, T2> fmt, T0&& arg0, T1&& arg1, T2&& arg2,
         log_internal::source_location location = log_internal::source_location()) {
-  vlog(log_internal::kFatal, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
-       fmt::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1),
+  vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
+       std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1),
                              log_internal::format_replace(arg2)));
   std::abort();  // Enforce [[noreturn]]
 }
 
 template <typename T0, typename T1, typename T2, typename T3>
 [[noreturn]] [[maybe_unused]] static void fatal(
-        fmt::format_string<T0, T1, T2, T3> fmt, T0&& arg0, T1&& arg1, T2&& arg2, T3&& arg3,
+        std::format_string<T0, T1, T2, T3> fmt, T0&& arg0, T1&& arg1, T2&& arg2, T3&& arg3,
         log_internal::source_location location = log_internal::source_location()) {
-  vlog(log_internal::kFatal, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
-       fmt::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1),
+  vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
+       std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1),
                              log_internal::format_replace(arg2),
                              log_internal::format_replace(arg3)));
   std::abort();  // Enforce [[noreturn]]
@@ -201,21 +200,63 @@
 
 template <typename... T>
 struct assert_that {
-  assert_that(bool cond, fmt::format_string<T...> fmt, T&&... args,
+  assert_that(bool cond, std::format_string<T...> fmt, T&&... args,
               log_internal::source_location location = log_internal::source_location()) {
     if (!cond) {
-      vlog(log_internal::kFatal, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
-           fmt::make_format_args(log_internal::format_replace(args)...));
+      vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
+           std::make_format_args(log_internal::format_replace(args)...));
     }
   }
 };
 
 template <typename... T>
-assert_that(bool, fmt::format_string<T...>, T&&...) -> assert_that<T...>;
+assert_that(bool, std::format_string<T...>, T&&...) -> assert_that<T...>;
 
 }  // namespace bluetooth::log
 
-namespace fmt {
+namespace std {
+
+/// Helper to format a pointer value as the memory address.
+/// Use this helper as `std::format("{}", std::format_ptr(value));`.
+template <typename T>
+const void* format_ptr(T* ptr) {
+  return reinterpret_cast<const void*>(ptr);
+}
+
+/// Derive formatter for std::atomic<T> types where T is formattable.
+/// The formatter uses the default memory order `std::memory_order_seq_cst`
+/// for reading the value.
+template <typename T, typename CharT>
+struct formatter<std::atomic<T>, CharT> : formatter<T, CharT> {
+  template <typename Context>
+  auto format(const std::atomic<T>& v, Context& ctx) const -> decltype(ctx.out()) {
+    return formatter<T, CharT>::format(v.load(), ctx);
+  }
+};
+
+/// Default formatter implementation for formatting
+/// types overloading the ostream `operator<<`.
+///
+/// Enable this formatter in the code by declaring:
+/// ```
+/// template<>
+/// struct std::formatter<T> : ostream_formatter {};
+/// ```
+template <typename CharT>
+struct basic_ostream_formatter : formatter<basic_string_view<CharT>, CharT> {
+  void set_debug_format() = delete;
+
+  template <typename T, typename Context>
+  auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
+    auto&& output = std::basic_stringstream<CharT>();
+    output.imbue(std::locale::classic());  // The default is always unlocalized.
+    output << value;
+    output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
+    return formatter<basic_string_view<CharT>, CharT>::format(output.str(), ctx);
+  }
+};
+
+using ostream_formatter = basic_ostream_formatter<char>;
 
 /// Default formatter implementation for formatting
 /// enum class values to the underlying type.
@@ -223,13 +264,13 @@
 /// Enable this formatter in the code by declaring:
 /// ```
 /// template<>
-/// struct fmt::formatter<EnumT> : enum_formatter<EnumT> {};
+/// struct std::formatter<EnumT> : enum_formatter<EnumT> {};
 /// ```
 template <typename EnumT, class CharT = char>
-struct enum_formatter : fmt::formatter<std::underlying_type_t<EnumT>, CharT> {
+struct enum_formatter : std::formatter<std::underlying_type_t<EnumT>, CharT> {
   template <class Context>
   typename Context::iterator format(EnumT value, Context& ctx) const {
-    return fmt::formatter<std::underlying_type_t<EnumT>, CharT>::format(
+    return std::formatter<std::underlying_type_t<EnumT>, CharT>::format(
             static_cast<std::underlying_type_t<EnumT>>(value), ctx);
   }
 };
@@ -241,14 +282,14 @@
 /// Enable this formatter in the code by declaring:
 /// ```
 /// template<>
-/// struct fmt::formatter<T> : string_formatter<T, &T_to_str> {};
+/// struct std::formatter<T> : string_formatter<T, &T_to_str> {};
 /// ```
 template <typename T, std::string (*F)(const T&), class CharT = char>
-struct string_formatter : fmt::formatter<std::string> {
+struct string_formatter : std::formatter<std::string> {
   template <class Context>
   typename Context::iterator format(const T& value, Context& ctx) const {
-    return fmt::formatter<std::string>::format(F(value), ctx);
+    return std::formatter<std::string>::format(F(value), ctx);
   }
 };
 
-}  // namespace fmt
+}  // namespace std
diff --git a/system/log/src/truncating_buffer_test.cc b/system/log/src/truncating_buffer_test.cc
index a0b4a09..3940a08 100644
--- a/system/log/src/truncating_buffer_test.cc
+++ b/system/log/src/truncating_buffer_test.cc
@@ -18,17 +18,18 @@
 
 #include "truncating_buffer.h"
 
-#include <fmt/format.h>
 #include <gtest/gtest.h>
 
+#include <format>
+
 using namespace bluetooth::log_internal;
 
 TEST(TruncatingBufferTest, 1byte) {
   EXPECT_EQ(sizeof("ab"), 3);
   truncating_buffer<2> buffer_1;
   truncating_buffer<3> buffer_2;
-  fmt::format_to(std::back_insert_iterator(buffer_1), "ab");
-  fmt::format_to(std::back_insert_iterator(buffer_2), "ab");
+  std::format_to(std::back_insert_iterator(buffer_1), "ab");
+  std::format_to(std::back_insert_iterator(buffer_2), "ab");
   EXPECT_STREQ(buffer_1.c_str(), "a");
   EXPECT_STREQ(buffer_2.c_str(), "ab");
 }
@@ -38,9 +39,9 @@
   truncating_buffer<3> buffer_1;
   truncating_buffer<4> buffer_2;
   truncating_buffer<5> buffer_3;
-  fmt::format_to(std::back_insert_iterator(buffer_1), "αβ");
-  fmt::format_to(std::back_insert_iterator(buffer_2), "αβ");
-  fmt::format_to(std::back_insert_iterator(buffer_3), "αβ");
+  std::format_to(std::back_insert_iterator(buffer_1), "αβ");
+  std::format_to(std::back_insert_iterator(buffer_2), "αβ");
+  std::format_to(std::back_insert_iterator(buffer_3), "αβ");
   EXPECT_STREQ(buffer_1.c_str(), "α");
   EXPECT_STREQ(buffer_2.c_str(), "α");
   EXPECT_STREQ(buffer_3.c_str(), "αβ");
@@ -52,10 +53,10 @@
   truncating_buffer<5> buffer_2;
   truncating_buffer<6> buffer_3;
   truncating_buffer<7> buffer_4;
-  fmt::format_to(std::back_insert_iterator(buffer_1), "ພຮ");
-  fmt::format_to(std::back_insert_iterator(buffer_2), "ພຮ");
-  fmt::format_to(std::back_insert_iterator(buffer_3), "ພຮ");
-  fmt::format_to(std::back_insert_iterator(buffer_4), "ພຮ");
+  std::format_to(std::back_insert_iterator(buffer_1), "ພຮ");
+  std::format_to(std::back_insert_iterator(buffer_2), "ພຮ");
+  std::format_to(std::back_insert_iterator(buffer_3), "ພຮ");
+  std::format_to(std::back_insert_iterator(buffer_4), "ພຮ");
   EXPECT_STREQ(buffer_1.c_str(), "ພ");
   EXPECT_STREQ(buffer_2.c_str(), "ພ");
   EXPECT_STREQ(buffer_3.c_str(), "ພ");
@@ -69,11 +70,11 @@
   truncating_buffer<7> buffer_3;
   truncating_buffer<8> buffer_4;
   truncating_buffer<9> buffer_5;
-  fmt::format_to(std::back_insert_iterator(buffer_1), "𐎡𐎪");
-  fmt::format_to(std::back_insert_iterator(buffer_2), "𐎡𐎪");
-  fmt::format_to(std::back_insert_iterator(buffer_3), "𐎡𐎪");
-  fmt::format_to(std::back_insert_iterator(buffer_4), "𐎡𐎪");
-  fmt::format_to(std::back_insert_iterator(buffer_5), "𐎡𐎪");
+  std::format_to(std::back_insert_iterator(buffer_1), "𐎡𐎪");
+  std::format_to(std::back_insert_iterator(buffer_2), "𐎡𐎪");
+  std::format_to(std::back_insert_iterator(buffer_3), "𐎡𐎪");
+  std::format_to(std::back_insert_iterator(buffer_4), "𐎡𐎪");
+  std::format_to(std::back_insert_iterator(buffer_5), "𐎡𐎪");
   EXPECT_STREQ(buffer_1.c_str(), "𐎡");
   EXPECT_STREQ(buffer_2.c_str(), "𐎡");
   EXPECT_STREQ(buffer_3.c_str(), "𐎡");
diff --git a/system/log/src/vlog_android.cc b/system/log/src/vlog_android.cc
index 6f4ef34..53b195c 100644
--- a/system/log/src/vlog_android.cc
+++ b/system/log/src/vlog_android.cc
@@ -25,8 +25,8 @@
 
 static constexpr size_t kBufferSize = 1024;
 
-void vlog(Level level, char const* tag, source_location location, fmt::string_view fmt,
-          fmt::format_args vargs) {
+void vlog(Level level, char const* tag, source_location location, std::string_view fmt,
+          std::format_args vargs) {
   // Check if log is enabled.
   if (!__android_log_is_loggable(level, "bluetooth", ANDROID_LOG_INFO)) {
     return;
@@ -44,9 +44,9 @@
   // In order to have consistent logs we include it manually in the log
   // message.
   truncating_buffer<kBufferSize> buffer;
-  fmt::format_to(std::back_insert_iterator(buffer), "{}:{} {}: ", file_name, location.line,
+  std::format_to(std::back_insert_iterator(buffer), "{}:{} {}: ", file_name, location.line,
                  location.function_name);
-  fmt::vformat_to(std::back_insert_iterator(buffer), fmt, vargs);
+  std::vformat_to(std::back_insert_iterator(buffer), fmt, vargs);
 
   // Send message to liblog.
   struct __android_log_message message = {
diff --git a/system/log/src/vlog_syslog.cc b/system/log/src/vlog_syslog.cc
index 73d7ec6..890fc39 100644
--- a/system/log/src/vlog_syslog.cc
+++ b/system/log/src/vlog_syslog.cc
@@ -48,8 +48,8 @@
 // Default value for $MaxMessageSize for rsyslog.
 static constexpr size_t kBufferSize = 8192;
 
-void vlog(Level level, char const* tag, source_location location, fmt::string_view fmt,
-          fmt::format_args vargs) {
+void vlog(Level level, char const* tag, source_location location, std::string_view fmt,
+          std::format_args vargs) {
   // Filter out logs that don't meet level requirement.
   Level current_level = GetLogLevelForTag(tag);
   if (level < current_level) {
@@ -82,11 +82,11 @@
   truncating_buffer<kBufferSize> buffer;
 
   // Format file, line.
-  fmt::format_to(std::back_insert_iterator(buffer), "{} {}:{} {}: ", tag, location.file_name,
+  std::format_to(std::back_insert_iterator(buffer), "{} {}:{} {}: ", tag, location.file_name,
                  location.line, location.function_name);
 
   // Format message.
-  fmt::vformat_to(std::back_insert_iterator(buffer), fmt, vargs);
+  std::vformat_to(std::back_insert_iterator(buffer), fmt, vargs);
 
   // Print to vsyslog.
   syslog(LOG_USER | severity, "%s", buffer.c_str());
diff --git a/system/main/shim/acl.cc b/system/main/shim/acl.cc
index 44c3a05..da5afc6 100644
--- a/system/main/shim/acl.cc
+++ b/system/main/shim/acl.cc
@@ -123,16 +123,16 @@
 };
 }  // namespace std
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<ConnectAddressWithType> : formatter<std::string> {
   template <class Context>
   typename Context::iterator format(const ConnectAddressWithType& address, Context& ctx) const {
     std::string repr = address.ToRedactedStringForLogging();
-    return fmt::formatter<std::string>::format(repr, ctx);
+    return std::formatter<std::string>::format(repr, ctx);
   }
 };
-}  // namespace fmt
+}  // namespace std
 
 namespace {
 
diff --git a/system/osi/include/compat.h b/system/osi/include/compat.h
index 78c2948..6fbefc3 100644
--- a/system/osi/include/compat.h
+++ b/system/osi/include/compat.h
@@ -25,7 +25,7 @@
 /// Supplied by bionic and glibc>=2.38
 /// This declaration is added simplify clang-tidy
 /// misc-include-cleaner check.
-extern "C" size_t strlcpy(char* dst, const char* src, size_t siz);
+size_t osi_strlcpy(char* dst, const char* src, size_t size);
 
 #if __GLIBC__
 
diff --git a/system/osi/src/compat.cc b/system/osi/src/compat.cc
index d4cbe5c..97e812a 100644
--- a/system/osi/src/compat.cc
+++ b/system/osi/src/compat.cc
@@ -54,17 +54,14 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+#endif /* __GLIBC__ */
 
 /*
- * Glibc added strlcpy() starting with 2.38, so skip it if glibc >= 2.38.
- */
-#if !(__GLIBC_PREREQ(2, 38))
-/*
  * Copy src to string dst of size siz.  At most siz-1 characters
  * will be copied.  Always NUL terminates (unless siz == 0).
  * Returns strlen(src); if retval >= siz, truncation occurred.
  */
-extern "C" size_t strlcpy(char* dst, const char* src, size_t siz) {
+size_t osi_strlcpy(char* dst, const char* src, size_t siz) {
   char* d = dst;
   const char* s = src;
   size_t n = siz;
@@ -89,5 +86,3 @@
 
   return s - src - 1; /* count does not include NUL */
 }
-#endif /* !(__GLIBC_PREREQ(2, 38)) */
-#endif /* __GLIBC__ */
diff --git a/system/osi/test/fuzzers/compat/fuzz_compat.cc b/system/osi/test/fuzzers/compat/fuzz_compat.cc
index cf209cc..3df5924 100644
--- a/system/osi/test/fuzzers/compat/fuzz_compat.cc
+++ b/system/osi/test/fuzzers/compat/fuzz_compat.cc
@@ -49,7 +49,7 @@
 
   // Copy, then concat
   size_t len_to_cpy = dataProvider.ConsumeIntegralInRange<size_t>(0, buf_size);
-  strlcpy(reinterpret_cast<char*>(dst_buf), reinterpret_cast<char*>(bytes.data()), len_to_cpy);
+  osi_strlcpy(reinterpret_cast<char*>(dst_buf), reinterpret_cast<char*>(bytes.data()), len_to_cpy);
 
   // Clear out our dest buffer
   free(dst_buf);
diff --git a/system/pdl/hci/include/hci/address.h b/system/pdl/hci/include/hci/address.h
index 8e27da1..bc9c8d9 100644
--- a/system/pdl/hci/include/hci/address.h
+++ b/system/pdl/hci/include/hci/address.h
@@ -114,15 +114,15 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::hci::Address> : formatter<std::string> {
   template <class Context>
   typename Context::iterator format(const bluetooth::hci::Address& address, Context& ctx) const {
     std::string repr = address.ToRedactedStringForLogging();
-    return fmt::formatter<std::string>::format(repr, ctx);
+    return std::formatter<std::string>::format(repr, ctx);
   }
 };
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
diff --git a/system/profile/avrcp/avrcp_sdp_service.h b/system/profile/avrcp/avrcp_sdp_service.h
index dace6b8..6f4b9ef 100644
--- a/system/profile/avrcp/avrcp_sdp_service.h
+++ b/system/profile/avrcp/avrcp_sdp_service.h
@@ -19,6 +19,7 @@
 #include <bluetooth/log.h>
 
 #include <cstdint>
+#include <memory>
 
 #include "avrcp_sdp_records.h"
 
diff --git a/system/profile/avrcp/device.cc b/system/profile/avrcp/device.cc
index 0201858..a5012e8 100644
--- a/system/profile/avrcp/device.cc
+++ b/system/profile/avrcp/device.cc
@@ -44,7 +44,7 @@
 extern bool btif_av_src_sink_coexist_enabled(void);
 
 template <>
-struct fmt::formatter<bluetooth::avrcp::PlayState> : enum_formatter<bluetooth::avrcp::PlayState> {};
+struct std::formatter<bluetooth::avrcp::PlayState> : enum_formatter<bluetooth::avrcp::PlayState> {};
 
 namespace bluetooth {
 namespace avrcp {
diff --git a/system/rust/src/gatt/ffi/gatt_shim.h b/system/rust/src/gatt/ffi/gatt_shim.h
index 0ddbab0..e159831 100644
--- a/system/rust/src/gatt/ffi/gatt_shim.h
+++ b/system/rust/src/gatt/ffi/gatt_shim.h
@@ -58,8 +58,8 @@
 }  // namespace gatt
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::gatt::AttributeBackingType>
     : enum_formatter<bluetooth::gatt::AttributeBackingType> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/a2dp/a2dp_aac_encoder.cc b/system/stack/a2dp/a2dp_aac_encoder.cc
index db455cd..841ef48 100644
--- a/system/stack/a2dp/a2dp_aac_encoder.cc
+++ b/system/stack/a2dp/a2dp_aac_encoder.cc
@@ -49,10 +49,10 @@
 
 using namespace bluetooth;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<AACENC_ERROR> : enum_formatter<AACENC_ERROR> {};
-}  // namespace fmt
+}  // namespace std
 
 typedef struct {
   uint32_t sample_rate;
diff --git a/system/stack/a2dp/a2dp_codec_config.cc b/system/stack/a2dp/a2dp_codec_config.cc
index 2469cbc..9155dab 100644
--- a/system/stack/a2dp/a2dp_codec_config.cc
+++ b/system/stack/a2dp/a2dp_codec_config.cc
@@ -1665,7 +1665,7 @@
       break;
   }
 
-  return fmt::format("Unsupported codec type: {:x}", codec_type);
+  return std::format("Unsupported codec type: {:x}", codec_type);
 }
 
 int A2DP_GetEecoderEffectiveFrameSize(const uint8_t* p_codec_info) {
diff --git a/system/stack/a2dp/a2dp_sbc_decoder.cc b/system/stack/a2dp/a2dp_sbc_decoder.cc
index b3d2881..d59f4ae 100644
--- a/system/stack/a2dp/a2dp_sbc_decoder.cc
+++ b/system/stack/a2dp/a2dp_sbc_decoder.cc
@@ -31,10 +31,10 @@
 
 using namespace bluetooth;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<OI_STATUS> : enum_formatter<OI_STATUS> {};
-}  // namespace fmt
+}  // namespace std
 
 typedef struct {
   OI_CODEC_SBC_DECODER_CONTEXT decoder_context;
diff --git a/system/stack/a2dp/a2dp_vendor.cc b/system/stack/a2dp/a2dp_vendor.cc
index 74f1b5c..28a27c7 100644
--- a/system/stack/a2dp/a2dp_vendor.cc
+++ b/system/stack/a2dp/a2dp_vendor.cc
@@ -541,5 +541,5 @@
 
   // Add checks based on <vendor_id, codec_id>
 
-  return fmt::format("Unsupported codec vendor_id: 0x{:x} codec_id: 0x{:x}", vendor_id, codec_id);
+  return std::format("Unsupported codec vendor_id: 0x{:x} codec_id: 0x{:x}", vendor_id, codec_id);
 }
diff --git a/system/stack/a2dp/a2dp_vendor_ldac_decoder.cc b/system/stack/a2dp/a2dp_vendor_ldac_decoder.cc
index 0571216..9c13d50 100644
--- a/system/stack/a2dp/a2dp_vendor_ldac_decoder.cc
+++ b/system/stack/a2dp/a2dp_vendor_ldac_decoder.cc
@@ -34,10 +34,10 @@
 
 using namespace bluetooth;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<LDACBT_SMPL_FMT_T> : enum_formatter<LDACBT_SMPL_FMT_T> {};
-}  // namespace fmt
+}  // namespace std
 
 //
 // Decoder for LDAC Source Codec
diff --git a/system/stack/a2dp/a2dp_vendor_ldac_encoder.cc b/system/stack/a2dp/a2dp_vendor_ldac_encoder.cc
index c2a04d6..4dd4a6e 100644
--- a/system/stack/a2dp/a2dp_vendor_ldac_encoder.cc
+++ b/system/stack/a2dp/a2dp_vendor_ldac_encoder.cc
@@ -61,10 +61,10 @@
 
 using namespace bluetooth;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<LDACBT_SMPL_FMT_T> : enum_formatter<LDACBT_SMPL_FMT_T> {};
-}  // namespace fmt
+}  // namespace std
 
 typedef struct {
   uint32_t sample_rate;
diff --git a/system/stack/avct/avct_api.cc b/system/stack/avct/avct_api.cc
index 2c3db70..33d5812 100644
--- a/system/stack/avct/avct_api.cc
+++ b/system/stack/avct/avct_api.cc
@@ -457,7 +457,7 @@
     if (ccb.p_lcb) {  // tAVCT_LCB
       LOG_DUMPSYS(fd,
                   "  Link  : peer:%s lcid:0x%04x sm_state:%-24s ch_state:%s conflict_lcid:0x%04x",
-                  fmt::format("{}", ccb.p_lcb->peer_addr).c_str(), ccb.p_lcb->ch_lcid,
+                  std::format("{}", ccb.p_lcb->peer_addr).c_str(), ccb.p_lcb->ch_lcid,
                   avct_sm_state_text(ccb.p_lcb->state).c_str(),
                   avct_ch_state_text(ccb.p_lcb->ch_state).c_str(), ccb.p_lcb->conflict_lcid);
     } else {
@@ -467,7 +467,7 @@
     if (ccb.p_bcb) {  // tAVCT_BCB
       LOG_DUMPSYS(fd,
                   "  Browse: peer:%s lcid:0x%04x sm_state:%-24s ch_state:%s conflict_lcid:0x%04x",
-                  fmt::format("{}", ccb.p_bcb->peer_addr).c_str(), ccb.p_bcb->ch_lcid,
+                  std::format("{}", ccb.p_bcb->peer_addr).c_str(), ccb.p_bcb->ch_lcid,
                   avct_sm_state_text(ccb.p_bcb->state).c_str(),
                   avct_ch_state_text(ccb.p_bcb->ch_state).c_str(), ccb.p_bcb->conflict_lcid);
     } else {
diff --git a/system/stack/avdt/avdt_ad.cc b/system/stack/avdt/avdt_ad.cc
index 92535cb..36b395d 100644
--- a/system/stack/avdt/avdt_ad.cc
+++ b/system/stack/avdt/avdt_ad.cc
@@ -289,8 +289,8 @@
   AvdtpScb* p_scb;
   tAVDT_SCB_TC_CLOSE close;
 
-  log::verbose("p_tbl: {} state: {} tcid: {} type: {} ccb_idx: {} scb_hdl: {}", fmt::ptr(p_tbl),
-               tc_state_text(p_tbl->state), p_tbl->tcid,
+  log::verbose("p_tbl: {} state: {} tcid: {} type: {} ccb_idx: {} scb_hdl: {}",
+               std::format_ptr(p_tbl), tc_state_text(p_tbl->state), p_tbl->tcid,
                tc_type_text(avdt_ad_tcid_to_type(p_tbl->tcid)), p_tbl->ccb_idx,
                avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
 
@@ -338,8 +338,8 @@
   tAVDT_OPEN open;
   tAVDT_EVT_HDR evt;
 
-  log::verbose("p_tbl: {} state: {} tcid: {} type: {} ccb_idx: {} scb_hdl: {}", fmt::ptr(p_tbl),
-               tc_state_text(p_tbl->state), p_tbl->tcid,
+  log::verbose("p_tbl: {} state: {} tcid: {} type: {} ccb_idx: {} scb_hdl: {}",
+               std::format_ptr(p_tbl), tc_state_text(p_tbl->state), p_tbl->tcid,
                tc_type_text(avdt_ad_tcid_to_type(p_tbl->tcid)), p_tbl->ccb_idx,
                avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
 
@@ -401,7 +401,7 @@
   AvdtpScb* p_scb;
 
   log::verbose("p_tbl: {} state: {} tcid: {} type: {} ccb_idx: {} scb_hdl: {} is_congested: {}",
-               fmt::ptr(p_tbl), tc_state_text(p_tbl->state), p_tbl->tcid,
+               std::format_ptr(p_tbl), tc_state_text(p_tbl->state), p_tbl->tcid,
                tc_type_text(avdt_ad_tcid_to_type(p_tbl->tcid)), p_tbl->ccb_idx,
                avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl, is_congested);
 
@@ -515,7 +515,7 @@
 
   p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb);
   p_tbl->my_mtu = kAvdtpMtu;
-  log::verbose("p_tbl: {} state: {} tcid: {} type: {} role: {} my_mtu: {}", fmt::ptr(p_tbl),
+  log::verbose("p_tbl: {} state: {} tcid: {} type: {} role: {} my_mtu: {}", std::format_ptr(p_tbl),
                tc_state_text(p_tbl->state), p_tbl->tcid, tc_type_text(type), role, p_tbl->my_mtu);
 
   if (type != AVDT_CHAN_SIG) {
@@ -574,9 +574,9 @@
   AvdtpTransportChannel* p_tbl;
 
   p_tbl = avdt_ad_tc_tbl_by_type(type, p_ccb, p_scb);
-  log::verbose("p_tbl: {} state: {} tcid: {} type: {} ccb_idx: {} scb_hdl: {}", fmt::ptr(p_tbl),
-               tc_state_text(p_tbl->state), p_tbl->tcid, tc_type_text(type), p_tbl->ccb_idx,
-               avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
+  log::verbose("p_tbl: {} state: {} tcid: {} type: {} ccb_idx: {} scb_hdl: {}",
+               std::format_ptr(p_tbl), tc_state_text(p_tbl->state), p_tbl->tcid, tc_type_text(type),
+               p_tbl->ccb_idx, avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
 
   switch (p_tbl->state) {
     case AVDT_AD_ST_UNUSED:
diff --git a/system/stack/avdt/avdt_ccb.cc b/system/stack/avdt/avdt_ccb.cc
index e478243..66eae28 100644
--- a/system/stack/avdt/avdt_ccb.cc
+++ b/system/stack/avdt/avdt_ccb.cc
@@ -366,7 +366,7 @@
   int i;
 
   log::verbose("CCB ccb={} event={} state={} p_ccb={}", avdt_ccb_to_idx(p_ccb),
-               avdt_ccb_evt_str[event], avdt_ccb_st_str[p_ccb->state], fmt::ptr(p_ccb));
+               avdt_ccb_evt_str[event], avdt_ccb_st_str[p_ccb->state], std::format_ptr(p_ccb));
 
   /* look up the state table for the current state */
   state_table = avdt_ccb_st_tbl[p_ccb->state];
@@ -457,7 +457,7 @@
   }
   p_ccb->Allocate(bd_addr);
   log::verbose("allocated (index {}) peer={} p_ccb={}", channel_index, p_ccb->peer_addr,
-               fmt::ptr(p_ccb));
+               std::format_ptr(p_ccb));
   return p_ccb;
 }
 
@@ -484,7 +484,7 @@
  ******************************************************************************/
 void avdt_ccb_dealloc(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* /* p_data */) {
   log::verbose("deallocated (index {}) peer={} p_ccb={}", avdt_ccb_to_idx(p_ccb), p_ccb->peer_addr,
-               fmt::ptr(p_ccb));
+               std::format_ptr(p_ccb));
   p_ccb->ResetCcb();
 }
 
diff --git a/system/stack/avdt/avdt_ccb_act.cc b/system/stack/avdt/avdt_ccb_act.cc
index 9f8557f..168d7ee 100644
--- a/system/stack/avdt/avdt_ccb_act.cc
+++ b/system/stack/avdt/avdt_ccb_act.cc
@@ -1026,7 +1026,7 @@
   tAVDT_CTRL avdt_ctrl;
 
   log::verbose("peer {} BtaAvScbIndex={} p_ccb={}", p_ccb->peer_addr, p_ccb->BtaAvScbIndex(),
-               fmt::ptr(p_ccb));
+               std::format_ptr(p_ccb));
   p_ccb->ll_opened = true;
 
   if (!p_ccb->p_conn_cback) {
diff --git a/system/stack/avdt/avdt_int.h b/system/stack/avdt/avdt_int.h
index 09eb424..09aa557 100644
--- a/system/stack/avdt/avdt_int.h
+++ b/system/stack/avdt/avdt_int.h
@@ -352,12 +352,12 @@
   }
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tTRANSPORT_CHANNEL_STATE> : enum_formatter<tTRANSPORT_CHANNEL_STATE> {};
 template <>
 struct formatter<tTRANSPORT_CHANNEL_TYPE> : enum_formatter<tTRANSPORT_CHANNEL_TYPE> {};
-}  // namespace fmt
+}  // namespace std
 
 /*****************************************************************************
  * data types
diff --git a/system/stack/avdt/avdt_scb.cc b/system/stack/avdt/avdt_scb.cc
index 67be1b9..6f4eefa 100644
--- a/system/stack/avdt/avdt_scb.cc
+++ b/system/stack/avdt/avdt_scb.cc
@@ -760,8 +760,8 @@
   uint8_t action;
 
   log::verbose("SCB hdl={} event={}/{} state={} p_avdt_scb={} scb_index={}", avdt_scb_to_hdl(p_scb),
-               event, avdt_scb_evt_str[event], avdt_scb_st_str[p_scb->state], fmt::ptr(p_scb),
-               p_scb->stream_config.scb_index);
+               event, avdt_scb_evt_str[event], avdt_scb_st_str[p_scb->state],
+               std::format_ptr(p_scb), p_scb->stream_config.scb_index);
 
   /* Check that we only send AVDT_SCB_API_WRITE_REQ_EVT to the active stream
    * device */
@@ -925,7 +925,7 @@
     return nullptr;
   }
 
-  log::verbose("SCB for handle {} found: p_scb={} scb_index={}", hdl, fmt::ptr(p_scb),
+  log::verbose("SCB for handle {} found: p_scb={} scb_index={}", hdl, std::format_ptr(p_scb),
                p_scb->stream_config.scb_index);
   return p_scb;
 }
diff --git a/system/stack/avdt/avdt_scb_act.cc b/system/stack/avdt/avdt_scb_act.cc
index 4f7c1f5..45a7a41 100644
--- a/system/stack/avdt/avdt_scb_act.cc
+++ b/system/stack/avdt/avdt_scb_act.cc
@@ -553,7 +553,7 @@
  *
  ******************************************************************************/
 void avdt_scb_hdl_setconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) {
-  log::verbose("p_scb->in_use={} p_avdt_scb={} scb_index={}", p_scb->in_use, fmt::ptr(p_scb),
+  log::verbose("p_scb->in_use={} p_avdt_scb={} scb_index={}", p_scb->in_use, std::format_ptr(p_scb),
                p_scb->stream_config.scb_index);
 
   if (p_scb->in_use) {
@@ -586,8 +586,8 @@
     log::error(
             "mismatch in AVDTP SCB/CCB state: (p_scb->p_ccb={} != p_ccb={}): "
             "p_scb={} scb_handle={} ccb_idx={}",
-            fmt::ptr(p_scb->p_ccb), fmt::ptr(p_ccb), fmt::ptr(p_scb), p_scb->ScbHandle(),
-            p_data->msg.config_cmd.hdr.ccb_idx);
+            std::format_ptr(p_scb->p_ccb), std::format_ptr(p_ccb), std::format_ptr(p_scb),
+            p_scb->ScbHandle(), p_data->msg.config_cmd.hdr.ccb_idx);
     avdt_scb_rej_not_in_use(p_scb, p_data);
     return;
   }
@@ -1016,7 +1016,7 @@
 void avdt_scb_snd_abort_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* /* p_data */) {
   tAVDT_EVT_HDR hdr;
 
-  log::verbose("p_scb->p_ccb={}", fmt::ptr(p_scb->p_ccb));
+  log::verbose("p_scb->p_ccb={}", std::format_ptr(p_scb->p_ccb));
 
   if (p_scb->p_ccb != NULL) {
     p_scb->role = AVDT_CLOSE_INT;
@@ -1286,8 +1286,8 @@
     log::error(
             "mismatch in AVDTP SCB/CCB state: (p_scb->p_ccb={} != p_ccb={}): "
             "p_scb={} scb_handle={} ccb_idx={}",
-            fmt::ptr(p_scb->p_ccb), fmt::ptr(p_ccb), fmt::ptr(p_scb), p_scb->ScbHandle(),
-            p_data->msg.config_cmd.hdr.ccb_idx);
+            std::format_ptr(p_scb->p_ccb), std::format_ptr(p_ccb), std::format_ptr(p_scb),
+            p_scb->ScbHandle(), p_data->msg.config_cmd.hdr.ccb_idx);
     avdt_scb_rej_not_in_use(p_scb, p_data);
     return;
   }
diff --git a/system/stack/avrc/avrc_api.cc b/system/stack/avrc/avrc_api.cc
index 273017d..13184ed 100644
--- a/system/stack/avrc/avrc_api.cc
+++ b/system/stack/avrc/avrc_api.cc
@@ -199,7 +199,7 @@
     p_next_cmd->layer_specific &= 0xFF;             /* AVCT_DATA_CTRL or AVCT_DATA_BROWSE */
 
     log::verbose("AVRC: Dequeuing command 0x{} (handle=0x{:02x}, label=0x{:02x})",
-                 fmt::ptr(p_next_cmd), handle, next_label);
+                 std::format_ptr(p_next_cmd), handle, next_label);
 
     /* Send the message */
     if ((AVCT_MsgReq(handle, next_label, AVCT_CMD, p_next_cmd)) == AVCT_SUCCESS) {
@@ -1309,8 +1309,8 @@
      * command
      * is received (exception is continuation request command
      * must sent that to get additional response frags) */
-    log::verbose("AVRC: Enqueuing command 0x{} (handle=0x{:02x}, label=0x{:02x})", fmt::ptr(p_pkt),
-                 handle, label);
+    log::verbose("AVRC: Enqueuing command 0x{} (handle=0x{:02x}, label=0x{:02x})",
+                 std::format_ptr(p_pkt), handle, label);
 
     /* label in BT_HDR (will need this later when the command is dequeued) */
     p_pkt->layer_specific = (label << 8) | (p_pkt->layer_specific & 0xFF);
diff --git a/system/stack/avrc/avrc_bld_ct.cc b/system/stack/avrc/avrc_bld_ct.cc
index 7ad5f17..d641607 100644
--- a/system/stack/avrc/avrc_bld_ct.cc
+++ b/system/stack/avrc/avrc_bld_ct.cc
@@ -582,8 +582,8 @@
   bool alloc = false;
   log::verbose("AVRC_BldCommand: pdu={:x} status={:x}", p_cmd->cmd.pdu, p_cmd->cmd.status);
   if (!p_cmd || !pp_pkt) {
-    log::verbose("AVRC_BldCommand. Invalid parameters passed. p_cmd={}, pp_pkt={}", fmt::ptr(p_cmd),
-                 fmt::ptr(pp_pkt));
+    log::verbose("AVRC_BldCommand. Invalid parameters passed. p_cmd={}, pp_pkt={}",
+                 std::format_ptr(p_cmd), std::format_ptr(pp_pkt));
     return AVRC_STS_BAD_PARAM;
   }
 
diff --git a/system/stack/avrc/avrc_bld_tg.cc b/system/stack/avrc/avrc_bld_tg.cc
index b90a833..d07ff65 100644
--- a/system/stack/avrc/avrc_bld_tg.cc
+++ b/system/stack/avrc/avrc_bld_tg.cc
@@ -66,7 +66,7 @@
   tAVRC_STS status = AVRC_STS_NO_ERROR;
 
   if (!(AVRC_IS_VALID_CAP_ID(p_rsp->capability_id))) {
-    log::error("bad parameter. p_rsp: {}", fmt::ptr(p_rsp));
+    log::error("bad parameter. p_rsp: {}", std::format_ptr(p_rsp));
     status = AVRC_STS_BAD_PARAM;
     return status;
   }
@@ -1359,8 +1359,8 @@
   uint16_t peer_mtu;
 
   if (!p_rsp || !pp_pkt) {
-    log::verbose("Invalid parameters passed. p_rsp={}, pp_pkt={}", fmt::ptr(p_rsp),
-                 fmt::ptr(pp_pkt));
+    log::verbose("Invalid parameters passed. p_rsp={}, pp_pkt={}", std::format_ptr(p_rsp),
+                 std::format_ptr(pp_pkt));
     return AVRC_STS_BAD_PARAM;
   }
 
diff --git a/system/stack/bnep/bnep_utils.cc b/system/stack/bnep/bnep_utils.cc
index 921e169..0740451 100644
--- a/system/stack/bnep/bnep_utils.cc
+++ b/system/stack/bnep/bnep_utils.cc
@@ -724,7 +724,8 @@
     if (rem_len != NULL) {
       *rem_len = 0;
     }
-    log::verbose("invalid packet: p = {} rem_len = {}", fmt::ptr(p), fmt::ptr(rem_len));
+    log::verbose("invalid packet: p = {} rem_len = {}", std::format_ptr(p),
+                 std::format_ptr(rem_len));
     return NULL;
   }
   uint16_t rem_len_orig = *rem_len;
diff --git a/system/stack/btm/btm_ble_sec.cc b/system/stack/btm/btm_ble_sec.cc
index 71a0aef..7ae82b3 100644
--- a/system/stack/btm/btm_ble_sec.cc
+++ b/system/stack/btm/btm_ble_sec.cc
@@ -102,7 +102,7 @@
     p_dev_rec->conn_params.peripheral_latency = BTM_BLE_CONN_PARAM_UNDEF;
 
     log::debug("Device added, handle=0x{:x}, p_dev_rec={}, bd_addr={}", p_dev_rec->ble_hci_handle,
-               fmt::ptr(p_dev_rec), bd_addr);
+               std::format_ptr(p_dev_rec), bd_addr);
 
     if (com::android::bluetooth::flags::name_discovery_for_le_pairing() &&
         btif_storage_get_stored_remote_name(bd_addr,
@@ -1793,7 +1793,7 @@
                            BTM_CMAC_TLEN_SIZE, p_mac);
   p_rec->sec_rec.increment_sign_counter(true);
 
-  log::verbose("p_mac = {}", fmt::ptr(p_mac));
+  log::verbose("p_mac = {}", std::format_ptr(p_mac));
   log::verbose("p_mac[0]=0x{:02x} p_mac[1]=0x{:02x} p_mac[2]=0x{:02x} p_mac[3]=0x{:02x}", *p_mac,
                *(p_mac + 1), *(p_mac + 2), *(p_mac + 3));
   log::verbose("p_mac[4]=0x{:02x} p_mac[5]=0x{:02x} p_mac[6]=0x{:02x} p_mac[7]=0x{:02x}",
diff --git a/system/stack/btm/btm_ble_sec.h b/system/stack/btm/btm_ble_sec.h
index 25e5e8f..cae6548 100644
--- a/system/stack/btm/btm_ble_sec.h
+++ b/system/stack/btm/btm_ble_sec.h
@@ -68,8 +68,8 @@
 tBTM_STATUS btm_ble_start_sec_check(const RawAddress& bd_addr, uint16_t psm, bool is_originator,
                                     tBTM_SEC_CALLBACK* p_callback, void* p_ref_data);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTM_BLE_SEC_REQ_ACT>
     : string_formatter<tBTM_BLE_SEC_REQ_ACT, &btm_ble_sec_req_act_text> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/btm/btm_dev.cc b/system/stack/btm/btm_dev.cc
index d4edde0..6712527 100644
--- a/system/stack/btm/btm_dev.cc
+++ b/system/stack/btm/btm_dev.cc
@@ -90,9 +90,9 @@
   if (!p_dev_rec) {
     p_dev_rec = btm_sec_allocate_dev_rec();
     log::info(
-            "Caching new record from config file device: {}, dev_class: 0x{:02x}, "
+            "Caching new record from config file device: {}, dev_class: {:02x}:{:02x}:{:02x}, "
             "link_key_type: 0x{:x}",
-            bd_addr, fmt::join(dev_class, ""), key_type);
+            bd_addr, dev_class[0], dev_class[1], dev_class[2], key_type);
 
     p_dev_rec->bd_addr = bd_addr;
     p_dev_rec->hci_handle =
@@ -109,9 +109,9 @@
     }
   } else {
     log::info(
-            "Caching existing record from config file device: {}, dev_class: "
-            "0x{:02x}, link_key_type: 0x{:x}",
-            bd_addr, fmt::join(dev_class, ""), key_type);
+            "Caching existing record from config file device: {},"
+            " dev_class: {:02x}:{:02x}:{:02x}, link_key_type: 0x{:x}",
+            bd_addr, dev_class[0], dev_class[1], dev_class[2], key_type);
 
     /* "Bump" timestamp for existing record */
     p_dev_rec->timestamp = btm_sec_cb.dev_rec_count++;
diff --git a/system/stack/btm/btm_iso_impl.h b/system/stack/btm/btm_iso_impl.h
index 5e563e9..cdda94f 100644
--- a/system/stack/btm/btm_iso_impl.h
+++ b/system/stack/btm/btm_iso_impl.h
@@ -95,11 +95,11 @@
   iso_impl() {
     iso_credits_ = shim::GetController()->GetControllerIsoBufferSize().total_num_le_packets_;
     iso_buffer_size_ = shim::GetController()->GetControllerIsoBufferSize().le_data_packet_length_;
-    log::info("{} created, iso credits: {}, buffer size: {}.", fmt::ptr(this), iso_credits_.load(),
-              iso_buffer_size_);
+    log::info("{} created, iso credits: {}, buffer size: {}.", std::format_ptr(this),
+              iso_credits_.load(), iso_buffer_size_);
   }
 
-  ~iso_impl() { log::info("{} removed.", fmt::ptr(this)); }
+  ~iso_impl() { log::info("{} removed.", std::format_ptr(this)); }
 
   void handle_register_cis_callbacks(CigCallbacks* callbacks) {
     log::assert_that(callbacks != nullptr, "Invalid CIG callbacks");
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index d19b9ae..7c329b4 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -396,7 +396,7 @@
  *
  ******************************************************************************/
 bool BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info) {
-  log::info("p_cb_info->p_le_callback == 0x{}", fmt::ptr(p_cb_info->p_le_callback));
+  log::info("p_cb_info->p_le_callback == 0x{}", std::format_ptr(p_cb_info->p_le_callback));
   if (p_cb_info->p_le_callback) {
     log::verbose("SMP_Register( btm_proc_smp_cback )");
     SMP_Register(btm_proc_smp_cback);
@@ -410,7 +410,7 @@
   }
 
   btm_sec_cb.api = *p_cb_info;
-  log::info("btm_sec_cb.api.p_le_callback = 0x{}", fmt::ptr(btm_sec_cb.api.p_le_callback));
+  log::info("btm_sec_cb.api.p_le_callback = 0x{}", std::format_ptr(btm_sec_cb.api.p_le_callback));
   log::verbose("application registered");
   return true;
 }
@@ -1611,8 +1611,8 @@
 
   rc = btm_sec_execute_procedure(p_dev_rec);
   if (rc != tBTM_STATUS::BTM_CMD_STARTED) {
-    log::verbose("p_dev_rec={}, clearing callback. old p_callback={}", fmt::ptr(p_dev_rec),
-                 fmt::ptr(p_dev_rec->sec_rec.p_callback));
+    log::verbose("p_dev_rec={}, clearing callback. old p_callback={}", std::format_ptr(p_dev_rec),
+                 std::format_ptr(p_dev_rec->sec_rec.p_callback));
     p_dev_rec->sec_rec.p_callback = NULL;
     (*p_callback)(bd_addr, transport, p_dev_rec->sec_rec.p_ref_data, rc);
   }
@@ -2048,8 +2048,8 @@
 
   p_dev_rec->sec_rec.classic_link = tSECURITY_STATE::IDLE;
 
-  log::verbose("clearing callback. p_dev_rec={}, p_callback={}", fmt::ptr(p_dev_rec),
-               fmt::ptr(p_dev_rec->sec_rec.p_callback));
+  log::verbose("clearing callback. p_dev_rec={}, p_callback={}", std::format_ptr(p_dev_rec),
+               std::format_ptr(p_dev_rec->sec_rec.p_callback));
   p_dev_rec->sec_rec.p_callback = NULL;
 }
 
@@ -2252,7 +2252,7 @@
   if ((btm_sec_cb.pairing_state == BTM_PAIR_STATE_WAIT_LOCAL_PIN) &&
       (btm_sec_cb.pairing_bda == bd_addr)) {
     log::verbose("delayed pin now being requested flags:0x{:x}, (p_pin_callback=0x{})",
-                 btm_sec_cb.pairing_flags, fmt::ptr(btm_sec_cb.api.p_pin_callback));
+                 btm_sec_cb.pairing_flags, std::format_ptr(btm_sec_cb.api.p_pin_callback));
 
     if ((btm_sec_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0 &&
         btm_sec_cb.api.p_pin_callback) {
@@ -3356,8 +3356,8 @@
   if (!p_dev_rec->sec_rec.is_security_state_bredr_encrypting()) {
     if (tSECURITY_STATE::DELAY_FOR_ENC == p_dev_rec->sec_rec.classic_link) {
       p_dev_rec->sec_rec.classic_link = tSECURITY_STATE::IDLE;
-      log::verbose("clearing callback. p_dev_rec={}, p_callback={}", fmt::ptr(p_dev_rec),
-                   fmt::ptr(p_dev_rec->sec_rec.p_callback));
+      log::verbose("clearing callback. p_dev_rec={}, p_callback={}", std::format_ptr(p_dev_rec),
+                   std::format_ptr(p_dev_rec->sec_rec.p_callback));
       p_dev_rec->sec_rec.p_callback = NULL;
       l2cu_resubmit_pending_sec_req(&p_dev_rec->bd_addr);
       return;
@@ -4364,7 +4364,8 @@
     log::warn(
             "btm_sec_pin_code_request(): Pairing disabled:{}; PIN callback:{}, Dev "
             "Rec:{}!",
-            p_cb->pairing_disabled, fmt::ptr(p_cb->api.p_pin_callback), fmt::ptr(p_dev_rec));
+            p_cb->pairing_disabled, std::format_ptr(p_cb->api.p_pin_callback),
+            std::format_ptr(p_dev_rec));
 
     btsnd_hcic_pin_code_neg_reply(p_bda);
   } else {
diff --git a/system/stack/btm/btm_sec_cb.cc b/system/stack/btm/btm_sec_cb.cc
index ac86e7f..c40d8fe 100644
--- a/system/stack/btm/btm_sec_cb.cc
+++ b/system/stack/btm/btm_sec_cb.cc
@@ -256,7 +256,7 @@
 
   if (is_originator) {
     p_srec->orig_mx_chan_id = mx_chan_id;
-    strlcpy((char*)p_srec->orig_service_name, p_name, BT_MAX_SERVICE_NAME_LEN + 1);
+    osi_strlcpy((char*)p_srec->orig_service_name, p_name, BT_MAX_SERVICE_NAME_LEN + 1);
     /* clear out the old setting, just in case it exists */
     {
       p_srec->security_flags &=
@@ -285,7 +285,7 @@
     p_out_serv = p_srec;
   } else {
     p_srec->term_mx_chan_id = mx_chan_id;
-    strlcpy((char*)p_srec->term_service_name, p_name, BT_MAX_SERVICE_NAME_LEN + 1);
+    osi_strlcpy((char*)p_srec->term_service_name, p_name, BT_MAX_SERVICE_NAME_LEN + 1);
     /* clear out the old setting, just in case it exists */
     {
       p_srec->security_flags &= ~(BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM |
diff --git a/system/stack/btm/btm_sec_int_types.h b/system/stack/btm/btm_sec_int_types.h
index 8d960d6..02f7b5a 100644
--- a/system/stack/btm/btm_sec_int_types.h
+++ b/system/stack/btm/btm_sec_int_types.h
@@ -100,7 +100,7 @@
 
 typedef uint8_t tBTM_SEC_ACTION;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTM_PAIRING_STATE> : enum_formatter<tBTM_PAIRING_STATE> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/btm/hfp_msbc_decoder.cc b/system/stack/btm/hfp_msbc_decoder.cc
index 21666b4..ccfbeea 100644
--- a/system/stack/btm/hfp_msbc_decoder.cc
+++ b/system/stack/btm/hfp_msbc_decoder.cc
@@ -32,10 +32,10 @@
 
 using namespace bluetooth;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<OI_STATUS> : enum_formatter<OI_STATUS> {};
-}  // namespace fmt
+}  // namespace std
 
 typedef struct {
   OI_CODEC_SBC_DECODER_CONTEXT decoder_context;
diff --git a/system/stack/btm/neighbor_inquiry.h b/system/stack/btm/neighbor_inquiry.h
index be4face..d4a13e9 100644
--- a/system/stack/btm/neighbor_inquiry.h
+++ b/system/stack/btm/neighbor_inquiry.h
@@ -235,7 +235,7 @@
 bool btm_inq_find_bdaddr(const RawAddress& p_bda);
 tINQ_DB_ENT* btm_inq_db_find(const RawAddress& p_bda);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTM_INQUIRY_CMPL::STATUS> : enum_formatter<tBTM_INQUIRY_CMPL::STATUS> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/btm/security_device_record.h b/system/stack/btm/security_device_record.h
index e660890..d72e1c0 100644
--- a/system/stack/btm/security_device_record.h
+++ b/system/stack/btm/security_device_record.h
@@ -381,9 +381,9 @@
   tBTM_SEC_REC sec_rec;
 };
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tSECURITY_STATE> : string_formatter<tSECURITY_STATE, &security_state_text> {};
 template <>
 struct formatter<tBLE_RAND_ADDR_TYPE> : enum_formatter<tBLE_RAND_ADDR_TYPE> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h
index 92c5cce..c746695 100644
--- a/system/stack/eatt/eatt_impl.h
+++ b/system/stack/eatt/eatt_impl.h
@@ -922,7 +922,8 @@
   }
 
   void upper_tester_connect(const RawAddress& bd_addr, eatt_device* eatt_dev, uint8_t role) {
-    log::info("L2CAP Upper tester enabled, {} ({}), role: {}({})", bd_addr, fmt::ptr(eatt_dev),
+    log::info("L2CAP Upper tester enabled, {} ({}), role: {}({})", bd_addr,
+              std::format_ptr(eatt_dev),
               role == HCI_ROLE_CENTRAL ? "HCI_ROLE_CENTRAL" : "HCI_ROLE_PERIPHERAL", role);
 
     auto num_of_chan = stack_config_get_interface()->get_pts_l2cap_ecoc_initial_chan_cnt();
diff --git a/system/stack/gatt/att_protocol.cc b/system/stack/gatt/att_protocol.cc
index 8360b81..2a5825b 100644
--- a/system/stack/gatt/att_protocol.cc
+++ b/system/stack/gatt/att_protocol.cc
@@ -470,7 +470,7 @@
 
   if (gatt_tcb_is_cid_busy(tcb, p_clcb->cid) && cmd_code != GATT_HANDLE_VALUE_CONF) {
     if (gatt_cmd_enq(tcb, p_clcb, true, cmd_code, p_cmd)) {
-      log::debug("Enqueued ATT command {} conn_id=0x{:04x}, cid={}", fmt::ptr(p_clcb),
+      log::debug("Enqueued ATT command {} conn_id=0x{:04x}, cid={}", std::format_ptr(p_clcb),
                  p_clcb->conn_id, p_clcb->cid);
       return GATT_CMD_STARTED;
     }
@@ -484,7 +484,7 @@
   tGATT_STATUS att_ret = attp_send_msg_to_l2cap(tcb, p_clcb->cid, p_cmd);
   if (att_ret != GATT_CONGESTED && att_ret != GATT_SUCCESS) {
     log::warn("Unable to send ATT command to l2cap layer {} conn_id=0x{:04x}, cid={}",
-              fmt::ptr(p_clcb), p_clcb->conn_id, p_clcb->cid);
+              std::format_ptr(p_clcb), p_clcb->conn_id, p_clcb->cid);
     return GATT_INTERNAL_ERROR;
   }
 
@@ -492,7 +492,7 @@
     return att_ret;
   }
 
-  log::debug("Starting ATT response timer {} conn_id=0x{:04x}, cid={}", fmt::ptr(p_clcb),
+  log::debug("Starting ATT response timer {} conn_id=0x{:04x}, cid={}", std::format_ptr(p_clcb),
              p_clcb->conn_id, p_clcb->cid);
   gatt_start_rsp_timer(p_clcb);
   if (!gatt_cmd_enq(tcb, p_clcb, false, cmd_code, NULL)) {
diff --git a/system/stack/gatt/gatt_cl.cc b/system/stack/gatt/gatt_cl.cc
index 6093d57..9a24181 100644
--- a/system/stack/gatt/gatt_cl.cc
+++ b/system/stack/gatt/gatt_cl.cc
@@ -1075,7 +1075,7 @@
         }
       } else /* exception, should not happen */
       {
-        log::error("attr offset = {} p_attr_buf = {}", offset, fmt::ptr(p_clcb->p_attr_buf));
+        log::error("attr offset = {} p_attr_buf = {}", offset, std::format_ptr(p_clcb->p_attr_buf));
         gatt_end_operation(p_clcb, GATT_NO_RESOURCES, (void*)p_clcb->p_attr_buf);
       }
     }
diff --git a/system/stack/gatt/gatt_int.h b/system/stack/gatt/gatt_int.h
index 88c8b88..3ceaa21 100644
--- a/system/stack/gatt/gatt_int.h
+++ b/system/stack/gatt/gatt_int.h
@@ -702,9 +702,9 @@
 }  // namespace legacy
 }  // namespace bluetooth
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tGATT_CH_STATE> : enum_formatter<tGATT_CH_STATE> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif
diff --git a/system/stack/gatt/gatt_sr.cc b/system/stack/gatt/gatt_sr.cc
index 8b67f2e..aad7b1f 100644
--- a/system/stack/gatt/gatt_sr.cc
+++ b/system/stack/gatt/gatt_sr.cc
@@ -145,7 +145,7 @@
   /* Double check in case any buffers are queued */
   log::verbose("gatt_dequeue_sr_cmd cid: 0x{:x}", cid);
   if (p_cmd->p_rsp_msg) {
-    log::error("free tcb.sr_cmd.p_rsp_msg = {}", fmt::ptr(p_cmd->p_rsp_msg));
+    log::error("free tcb.sr_cmd.p_rsp_msg = {}", std::format_ptr(p_cmd->p_rsp_msg));
   }
   osi_free_and_reset((void**)&p_cmd->p_rsp_msg);
 
diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc
index 73e08dc..73880e3 100644
--- a/system/stack/gatt/gatt_utils.cc
+++ b/system/stack/gatt/gatt_utils.cc
@@ -1254,8 +1254,8 @@
   if (!p_tcb->pending_enc_clcb.empty()) {
     for (size_t i = 0; i < p_tcb->pending_enc_clcb.size(); i++) {
       if (p_tcb->pending_enc_clcb.at(i) == p_clcb) {
-        log::warn("Removing clcb ({}) for conn id=0x{:04x} from pending_enc_clcb", fmt::ptr(p_clcb),
-                  p_clcb->conn_id);
+        log::warn("Removing clcb ({}) for conn id=0x{:04x} from pending_enc_clcb",
+                  std::format_ptr(p_clcb), p_clcb->conn_id);
         p_tcb->pending_enc_clcb.at(i) = NULL;
         break;
       }
@@ -1287,14 +1287,14 @@
   if (iter->to_send) {
     /* If command was not send, just remove the entire element */
     cl_cmd_q_p->erase(iter);
-    log::warn("Removing scheduled clcb ({}) for conn_id=0x{:04x}", fmt::ptr(p_clcb),
+    log::warn("Removing scheduled clcb ({}) for conn_id=0x{:04x}", std::format_ptr(p_clcb),
               p_clcb->conn_id);
   } else {
     /* If command has been sent, just invalidate p_clcb pointer for proper
      * response handling */
     iter->p_clcb = NULL;
     log::warn("Invalidating clcb ({}) for already sent request on conn_id=0x{:04x}",
-              fmt::ptr(p_clcb), p_clcb->conn_id);
+              std::format_ptr(p_clcb), p_clcb->conn_id);
   }
 }
 /*******************************************************************************
@@ -1815,7 +1815,7 @@
     (*p_cmpl_cb)(conn_id, op, status, &cb_data);
   } else {
     log::warn("not sent out op={} p_disc_cmpl_cb:{} p_cmpl_cb:{}", operation,
-              fmt::ptr(p_disc_cmpl_cb), fmt::ptr(p_cmpl_cb));
+              std::format_ptr(p_disc_cmpl_cb), std::format_ptr(p_cmpl_cb));
   }
 }
 
diff --git a/system/stack/hid/hid_conn.h b/system/stack/hid/hid_conn.h
index 77d0fa6..1611db4 100644
--- a/system/stack/hid/hid_conn.h
+++ b/system/stack/hid/hid_conn.h
@@ -78,9 +78,9 @@
 
 #define HIDD_SEC_CHN 3
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tHID_CONN_STATE> : enum_formatter<tHID_CONN_STATE> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif
diff --git a/system/stack/include/a2dp_constants.h b/system/stack/include/a2dp_constants.h
index 090c25a..e766fff 100644
--- a/system/stack/include/a2dp_constants.h
+++ b/system/stack/include/a2dp_constants.h
@@ -167,11 +167,11 @@
   A2DP_NOT_SUPPORTED_CODEC_PARAMETER = 0xE3,
 };
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::a2dp::CodecId> : enum_formatter<bluetooth::a2dp::CodecId> {};
 template <>
 struct formatter<tA2DP_CODEC_TYPE> : enum_formatter<tA2DP_CODEC_TYPE> {};
 template <>
 struct formatter<tA2DP_STATUS> : enum_formatter<tA2DP_STATUS> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/include/bt_name.h b/system/stack/include/bt_name.h
index c9749f0..bf4119b 100644
--- a/system/stack/include/bt_name.h
+++ b/system/stack/include/bt_name.h
@@ -28,8 +28,8 @@
 constexpr size_t kBdNameLength = static_cast<size_t>(BD_NAME_LEN);
 
 inline size_t bd_name_copy(BD_NAME bd_name_dest, const BD_NAME bd_name_src) {
-  return strlcpy(reinterpret_cast<char*>(bd_name_dest), reinterpret_cast<const char*>(bd_name_src),
-                 kBdNameLength + 1);
+  return osi_strlcpy(reinterpret_cast<char*>(bd_name_dest),
+                     reinterpret_cast<const char*>(bd_name_src), kBdNameLength + 1);
 }
 inline void bd_name_clear(BD_NAME bd_name) { *bd_name = {0}; }
 inline bool bd_name_is_empty(const BD_NAME bd_name) { return bd_name[0] == '\0'; }
@@ -40,7 +40,8 @@
     return;
   }
 
-  size_t src_len = strlcpy(reinterpret_cast<char*>(bd_name_dest), bd_name_char, sizeof(BD_NAME));
+  size_t src_len =
+          osi_strlcpy(reinterpret_cast<char*>(bd_name_dest), bd_name_char, sizeof(BD_NAME));
   if (src_len < sizeof(BD_NAME) - 1) {
     /* Zero the remaining destination memory */
     memset(bd_name_dest + src_len, 0, sizeof(BD_NAME) - src_len);
diff --git a/system/stack/include/bt_psm_types.h b/system/stack/include/bt_psm_types.h
index 3f07161..a781188 100644
--- a/system/stack/include/bt_psm_types.h
+++ b/system/stack/include/bt_psm_types.h
@@ -70,7 +70,7 @@
 
 inline std::string psm_to_text(uint16_t psm) { return bt_psm_text(static_cast<tBT_PSM>(psm)); }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBT_PSM> : enum_formatter<tBT_PSM> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/include/btm_api_types.h b/system/stack/include/btm_api_types.h
index 3f167bb..2c2ccf2 100644
--- a/system/stack/include/btm_api_types.h
+++ b/system/stack/include/btm_api_types.h
@@ -197,7 +197,7 @@
     CASE_RETURN_TEXT(tBTA_AG_UUID_CODEC::UUID_CODEC_MSBC);
     CASE_RETURN_TEXT(tBTA_AG_UUID_CODEC::UUID_CODEC_LC3);
     default:
-      return fmt::format("UNKNOWN Codec with id {}",
+      return std::format("UNKNOWN Codec with id {}",
                          static_cast<std::underlying_type_t<tBTA_AG_UUID_CODEC>>(result));
   }
 }
diff --git a/system/stack/include/btm_ble_api_types.h b/system/stack/include/btm_ble_api_types.h
index db5ae7a..9a16797 100644
--- a/system/stack/include/btm_ble_api_types.h
+++ b/system/stack/include/btm_ble_api_types.h
@@ -517,9 +517,9 @@
 
 typedef void(tBTM_BLE_CTRL_FEATURES_CBACK)(tHCI_STATUS status);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTM_BLE_CONN_TYPE> : enum_formatter<tBTM_BLE_CONN_TYPE> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // BTM_BLE_API_TYPES_H
diff --git a/system/stack/include/btm_sec_api_types.h b/system/stack/include/btm_sec_api_types.h
index 6b21574..d04974f2 100644
--- a/system/stack/include/btm_sec_api_types.h
+++ b/system/stack/include/btm_sec_api_types.h
@@ -525,9 +525,9 @@
   }
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTM_BLE_SEC_ACT> : enum_formatter<tBTM_BLE_SEC_ACT> {};
 template <>
 struct formatter<tBTM_BOND_TYPE> : enum_formatter<tBTM_BOND_TYPE> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/include/btm_status.h b/system/stack/include/btm_status.h
index 97247dc..a33a4b6 100644
--- a/system/stack/include/btm_status.h
+++ b/system/stack/include/btm_status.h
@@ -98,7 +98,7 @@
   }
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBTM_STATUS> : enum_formatter<tBTM_STATUS> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/include/gatt_api.h b/system/stack/include/gatt_api.h
index 804d639..3b751e1 100644
--- a/system/stack/include/gatt_api.h
+++ b/system/stack/include/gatt_api.h
@@ -1287,7 +1287,7 @@
 
 void gatt_tcb_dump(int fd);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<GattStatus> : enum_formatter<GattStatus> {};
 template <>
@@ -1298,6 +1298,6 @@
 struct formatter<tGATT_OP_CODE> : enum_formatter<tGATT_OP_CODE> {};
 template <>
 struct formatter<tGATT_DISC_TYPE> : enum_formatter<tGATT_DISC_TYPE> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* GATT_API_H */
diff --git a/system/stack/include/hci_error_code.h b/system/stack/include/hci_error_code.h
index 04ebb47..3d4b50c 100644
--- a/system/stack/include/hci_error_code.h
+++ b/system/stack/include/hci_error_code.h
@@ -247,7 +247,7 @@
   return static_cast<tHCI_REASON>(reason_code);
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tHCI_ERROR_CODE> : enum_formatter<tHCI_ERROR_CODE> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/include/hiddefs.h b/system/stack/include/hiddefs.h
index 4e8743d..583c302 100644
--- a/system/stack/include/hiddefs.h
+++ b/system/stack/include/hiddefs.h
@@ -181,9 +181,9 @@
   tSDP_DISC_REC* p_sdp_layer_rec;
 } tHID_DEV_SDP_INFO;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tHID_STATUS> : enum_formatter<tHID_STATUS> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif
diff --git a/system/stack/include/l2cap_types.h b/system/stack/include/l2cap_types.h
index e0927e9..311dab7 100644
--- a/system/stack/include/l2cap_types.h
+++ b/system/stack/include/l2cap_types.h
@@ -163,9 +163,9 @@
 constexpr uint16_t L2CAP_LE_MAX_MPS = 65533;
 constexpr uint16_t L2CAP_LE_CREDIT_MAX = 65535;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tL2CAP_LATENCY> : enum_formatter<tL2CAP_LATENCY> {};
 template <>
 struct formatter<tL2CAP_PRIORITY> : enum_formatter<tL2CAP_PRIORITY> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/include/l2cdefs.h b/system/stack/include/l2cdefs.h
index b598529..88ed7a7 100644
--- a/system/stack/include/l2cdefs.h
+++ b/system/stack/include/l2cdefs.h
@@ -545,11 +545,11 @@
 /* Mask for sequence numbers (range 0 - 63) */
 #define L2CAP_FCR_SEQ_MODULO 0x3F
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tL2CAP_CONN> : enum_formatter<tL2CAP_CONN> {};
 template <>
 struct formatter<tL2CAP_CID_FIXED> : enum_formatter<tL2CAP_CID_FIXED> {};
 template <>
 struct formatter<tL2CAP_LE_RESULT_CODE> : enum_formatter<tL2CAP_LE_RESULT_CODE> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/include/pan_api.h b/system/stack/include/pan_api.h
index 677ab44..9246f2a 100644
--- a/system/stack/include/pan_api.h
+++ b/system/stack/include/pan_api.h
@@ -422,9 +422,9 @@
 
 void PAN_Dumpsys(int fd);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tPAN_RESULT> : enum_formatter<tPAN_RESULT> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* PAN_API_H */
diff --git a/system/stack/include/port_api.h b/system/stack/include/port_api.h
index ae511a4..e298674 100644
--- a/system/stack/include/port_api.h
+++ b/system/stack/include/port_api.h
@@ -179,10 +179,10 @@
   RETURN_UNKNOWN_TYPE_STRING(tPORT_RESULT, result);
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tPORT_RESULT> : enum_formatter<tPORT_RESULT> {};
-}  // namespace fmt
+}  // namespace std
 
 typedef void(tPORT_MGMT_CALLBACK)(const tPORT_RESULT code, uint16_t port_handle);
 
diff --git a/system/stack/include/sdp_status.h b/system/stack/include/sdp_status.h
index d709dd5..cebcd96 100644
--- a/system/stack/include/sdp_status.h
+++ b/system/stack/include/sdp_status.h
@@ -87,7 +87,7 @@
 }
 const auto sdp_result_text = sdp_status_text;
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tSDP_STATUS> : enum_formatter<tSDP_STATUS> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/include/smp_api_types.h b/system/stack/include/smp_api_types.h
index 693faa8..922de8b 100644
--- a/system/stack/include/smp_api_types.h
+++ b/system/stack/include/smp_api_types.h
@@ -228,13 +228,13 @@
  * Manager requires verification from CSIP.*/
 typedef tBTM_STATUS(tSMP_SIRK_CALLBACK)(const RawAddress& bd_addr);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tSMP_OOB_DATA_TYPE> : enum_formatter<tSMP_OOB_DATA_TYPE> {};
 template <>
 struct formatter<tSMP_SEC_LEVEL> : enum_formatter<tSMP_SEC_LEVEL> {};
 template <>
 struct formatter<tSMP_EVT> : enum_formatter<tSMP_EVT> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // SMP_API_TYPES_H
diff --git a/system/stack/include/smp_status.h b/system/stack/include/smp_status.h
index dce8778..5c8b6f1 100644
--- a/system/stack/include/smp_status.h
+++ b/system/stack/include/smp_status.h
@@ -95,7 +95,7 @@
   }
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tSMP_STATUS> : enum_formatter<tSMP_STATUS> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc
index 99b0276..2b67132 100644
--- a/system/stack/l2cap/l2c_api.cc
+++ b/system/stack/l2cap/l2c_api.cc
@@ -320,7 +320,7 @@
     p_lcb = l2cu_allocate_lcb(p_bd_addr, false, BT_TRANSPORT_BR_EDR);
     /* currently use BR/EDR for ERTM mode l2cap connection */
     if (p_lcb == nullptr) {
-      log::warn("connection not started for PSM=0x{:x}, p_lcb={}", psm, fmt::ptr(p_lcb));
+      log::warn("connection not started for PSM=0x{:x}, p_lcb={}", psm, std::format_ptr(p_lcb));
       return 0;
     }
     l2cu_create_conn_br_edr(p_lcb);
@@ -520,7 +520,7 @@
     if ((p_lcb == NULL)
         /* currently use BR/EDR for ERTM mode l2cap connection */
         || (!l2cu_create_conn_le(p_lcb))) {
-      log::warn("conn not started for PSM: 0x{:04x}  p_lcb: 0x{}", psm, fmt::ptr(p_lcb));
+      log::warn("conn not started for PSM: 0x{:04x}  p_lcb: 0x{}", psm, std::format_ptr(p_lcb));
       return 0;
     }
   }
@@ -1540,7 +1540,7 @@
             "L2CA_FlushChannel (FLUSH)  CID: 0x{:04x}  NumToFlush: {}  QC: {}  "
             "pFirst: 0x{}",
             lcid, num_to_flush, fixed_queue_length(p_ccb->xmit_hold_q),
-            fmt::ptr(fixed_queue_try_peek_first(p_ccb->xmit_hold_q)));
+            std::format_ptr(fixed_queue_try_peek_first(p_ccb->xmit_hold_q)));
   } else {
     log::verbose("L2CA_FlushChannel (QUERY)  CID: 0x{:04x}", lcid);
   }
diff --git a/system/stack/l2cap/l2c_csm.cc b/system/stack/l2cap/l2c_csm.cc
index ba43876..42b451c 100644
--- a/system/stack/l2cap/l2c_csm.cc
+++ b/system/stack/l2cap/l2c_csm.cc
@@ -1688,7 +1688,8 @@
     log::error(
             "empty queue: p_ccb = {} p_ccb->in_use = {} p_ccb->chnl_state = {} "
             "p_ccb->local_cid = {} p_ccb->remote_cid = {}",
-            fmt::ptr(p_ccb), p_ccb->in_use, p_ccb->chnl_state, p_ccb->local_cid, p_ccb->remote_cid);
+            std::format_ptr(p_ccb), p_ccb->in_use, p_ccb->chnl_state, p_ccb->local_cid,
+            p_ccb->remote_cid);
   } else {
     fixed_queue_enqueue(p_ccb->xmit_hold_q, p_buf);
   }
diff --git a/system/stack/l2cap/l2c_int.h b/system/stack/l2cap/l2c_int.h
index 02a1fa6..0077cab 100644
--- a/system/stack/l2cap/l2c_int.h
+++ b/system/stack/l2cap/l2c_int.h
@@ -854,11 +854,11 @@
                                        uint16_t peripheral_latency, uint16_t cont_num,
                                        uint16_t timeout);
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tL2C_LINK_STATE> : enum_formatter<tL2C_LINK_STATE> {};
 template <>
 struct formatter<tL2CEVT> : enum_formatter<tL2CEVT> {};
 template <>
 struct formatter<tL2C_CHNL_STATE> : enum_formatter<tL2C_CHNL_STATE> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/l2cap/l2c_utils.cc b/system/stack/l2cap/l2c_utils.cc
index 0e923a5..3a057ec 100644
--- a/system/stack/l2cap/l2c_utils.cc
+++ b/system/stack/l2cap/l2c_utils.cc
@@ -1165,7 +1165,7 @@
 
   if ((!p_ccb->in_use) || (p_q == NULL)) {
     log::error("CID: 0x{:04x} ERROR in_use: {}  p_lcb: {}", p_ccb->local_cid, p_ccb->in_use,
-               fmt::ptr(p_ccb->p_lcb));
+               std::format_ptr(p_ccb->p_lcb));
     return;
   }
 
@@ -1251,8 +1251,8 @@
     log::error(
             "l2cu_dequeue_ccb  CID: 0x{:04x} ERROR in_use: {}  p_lcb: 0x{}  p_q: "
             "0x{}  p_q->p_first_ccb: 0x{}",
-            p_ccb->local_cid, p_ccb->in_use, fmt::ptr(p_ccb->p_lcb), fmt::ptr(p_q),
-            fmt::ptr(p_q ? p_q->p_first_ccb : 0));
+            p_ccb->local_cid, p_ccb->in_use, std::format_ptr(p_ccb->p_lcb), std::format_ptr(p_q),
+            std::format_ptr(p_q ? p_q->p_first_ccb : 0));
     return;
   }
 
@@ -2602,7 +2602,7 @@
   tL2C_CCB* p_next_ccb;
   int xx;
 
-  log::verbose("l2cu_resubmit_pending_sec_req  p_bda: 0x{}", fmt::ptr(p_bda));
+  log::verbose("l2cu_resubmit_pending_sec_req  p_bda: 0x{}", std::format_ptr(p_bda));
 
   /* If we are called with a BDA, only resubmit for that BDA */
   if (p_bda) {
diff --git a/system/stack/pan/pan_int.h b/system/stack/pan/pan_int.h
index f64f2ae..32d832a 100644
--- a/system/stack/pan/pan_int.h
+++ b/system/stack/pan/pan_int.h
@@ -128,9 +128,9 @@
 
 /******************************************************************************/
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tPAN_STATE> : enum_formatter<tPAN_STATE> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif
diff --git a/system/stack/rfcomm/port_api.cc b/system/stack/rfcomm/port_api.cc
index 6676eac..6786e88 100644
--- a/system/stack/rfcomm/port_api.cc
+++ b/system/stack/rfcomm/port_api.cc
@@ -143,7 +143,7 @@
                 "bd_addr={}, scn={}, is_server={}, mtu={}, uuid=0x{:x}, dlci={}, p_mcb={}, port={}",
                 static_cast<int>(p_port->state), static_cast<int>(p_port->rfc.state),
                 p_port->rfc.p_mcb ? p_port->rfc.p_mcb->state : 0, bd_addr, scn, is_server, mtu,
-                uuid, dlci, fmt::ptr(p_mcb), p_port->handle);
+                uuid, dlci, std::format_ptr(p_mcb), p_port->handle);
         *p_handle = p_port->handle;
         return PORT_ALREADY_OPENED;
       }
@@ -214,7 +214,8 @@
   log::info(
           "bd_addr={}, scn={}, is_server={}, mtu={}, uuid=0x{:x}, dlci={}, "
           "signal_state=0x{:x}, p_port={}",
-          bd_addr, scn, is_server, mtu, uuid, dlci, p_port->default_signal_state, fmt::ptr(p_port));
+          bd_addr, scn, is_server, mtu, uuid, dlci, p_port->default_signal_state,
+          std::format_ptr(p_port));
 
   // If this is not initiator of the connection need to just wait
   if (p_port->is_server) {
@@ -410,7 +411,7 @@
  *
  ******************************************************************************/
 int PORT_SetDataCOCallback(uint16_t handle, tPORT_DATA_CO_CALLBACK* p_port_cb) {
-  log::verbose("handle:{} cb 0x{}", handle, fmt::ptr(p_port_cb));
+  log::verbose("handle:{} cb 0x{}", handle, std::format_ptr(p_port_cb));
 
   tPORT* p_port = get_port_from_handle(handle);
   if (p_port == nullptr) {
@@ -445,7 +446,7 @@
     return PORT_BAD_HANDLE;
   }
   log::verbose("handle={}, in_use={}, port_state={}, p_mcb={}, peer_ready={}, rfc_state={}", handle,
-               p_port->in_use, p_port->state, fmt::ptr(p_port->rfc.p_mcb),
+               p_port->in_use, p_port->state, std::format_ptr(p_port->rfc.p_mcb),
                p_port->rfc.p_mcb ? p_port->rfc.p_mcb->peer_ready : -1, p_port->rfc.state);
 
   if (!p_port->in_use || (p_port->state == PORT_CONNECTION_STATE_CLOSED)) {
diff --git a/system/stack/rfcomm/port_int.h b/system/stack/rfcomm/port_int.h
index f4773d5..8fcbf84 100644
--- a/system/stack/rfcomm/port_int.h
+++ b/system/stack/rfcomm/port_int.h
@@ -141,10 +141,10 @@
   RETURN_UNKNOWN_TYPE_STRING(tPORT_CONNECTION_STATE, state);
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tPORT_CONNECTION_STATE> : enum_formatter<tPORT_CONNECTION_STATE> {};
-}  // namespace fmt
+}  // namespace std
 
 /*
  * Define control block containing information about PORT connection
diff --git a/system/stack/rfcomm/port_rfc.cc b/system/stack/rfcomm/port_rfc.cc
index be15fb1..1f8ba0b 100644
--- a/system/stack/rfcomm/port_rfc.cc
+++ b/system/stack/rfcomm/port_rfc.cc
@@ -65,7 +65,7 @@
  *
  ******************************************************************************/
 int port_open_continue(tPORT* p_port) {
-  log::verbose("port_open_continue, p_port:{}", fmt::ptr(p_port));
+  log::verbose("port_open_continue, p_port:{}", std::format_ptr(p_port));
 
   /* Check if multiplexer channel has already been established */
   tRFC_MCB* p_mcb = rfc_alloc_multiplexer_channel(p_port->bd_addr, true);
@@ -263,7 +263,8 @@
   p_port = &rfc_cb.port.port[0];
   for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) {
     if ((p_port->rfc.p_mcb == NULL) || (p_port->rfc.p_mcb == p_mcb)) {
-      log::verbose("PORT_StartInd, RFCOMM_StartRsp RFCOMM_SUCCESS: p_mcb:{}", fmt::ptr(p_mcb));
+      log::verbose("PORT_StartInd, RFCOMM_StartRsp RFCOMM_SUCCESS: p_mcb:{}",
+                   std::format_ptr(p_mcb));
       RFCOMM_StartRsp(p_mcb, RFCOMM_SUCCESS);
       return;
     }
@@ -289,7 +290,7 @@
     p_port = port_find_dlci_port(dlci);
     if (!p_port) {
       log::error("Disconnect RFCOMM, port not found, dlci={}, p_mcb={}, bd_addr={}", dlci,
-                 fmt::ptr(p_mcb), p_mcb->bd_addr);
+                 std::format_ptr(p_mcb), p_mcb->bd_addr);
       /* If the port cannot be opened, send a DM.  Per Errata 1205 */
       rfc_send_dm(p_mcb, dlci, false);
       /* check if this is the last port open, some headsets have
@@ -416,8 +417,8 @@
 void PORT_DlcEstablishInd(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu) {
   tPORT* p_port = port_find_mcb_dlci_port(p_mcb, dlci);
 
-  log::verbose("p_mcb:{}, dlci:{} mtu:{}i, p_port:{}, bd_addr:{}", fmt::ptr(p_mcb), dlci, mtu,
-               fmt::ptr(p_port), p_mcb->bd_addr);
+  log::verbose("p_mcb:{}, dlci:{} mtu:{}i, p_port:{}, bd_addr:{}", std::format_ptr(p_mcb), dlci,
+               mtu, std::format_ptr(p_port), p_mcb->bd_addr);
 
   if (!p_port) {
     /* This can be a first request for this port */
@@ -787,7 +788,7 @@
   int i;
 
   log::verbose("PORT_DataInd with data length {}, p_mcb:{},p_port:{},dlci:{}", p_buf->len,
-               fmt::ptr(p_mcb), fmt::ptr(p_port), dlci);
+               std::format_ptr(p_mcb), std::format_ptr(p_port), dlci);
   if (!p_port) {
     osi_free(p_buf);
     return;
diff --git a/system/stack/rfcomm/port_utils.cc b/system/stack/rfcomm/port_utils.cc
index 88276d0..25e1da9 100644
--- a/system/stack/rfcomm/port_utils.cc
+++ b/system/stack/rfcomm/port_utils.cc
@@ -86,7 +86,7 @@
       p_port->bd_addr = bd_addr;
       rfc_cb.rfc.last_port_index = port_index;
       log::verbose("rfc_cb.port.port[{}]:{} chosen, last_port_index:{}, bd_addr={}", port_index,
-                   fmt::ptr(p_port), rfc_cb.rfc.last_port_index, bd_addr);
+                   std::format_ptr(p_port), rfc_cb.rfc.last_port_index, bd_addr);
       return p_port;
     }
   }
@@ -199,7 +199,7 @@
  *
  ******************************************************************************/
 void port_release_port(tPORT* p_port) {
-  log::verbose("p_port: {} state: {} keep_handle: {}", fmt::ptr(p_port), p_port->rfc.state,
+  log::verbose("p_port: {} state: {} keep_handle: {}", std::format_ptr(p_port), p_port->rfc.state,
                p_port->keep_port_handle);
 
   mutex_global_lock();
@@ -280,7 +280,8 @@
   for (tRFC_MCB& mcb : rfc_cb.port.rfc_mcb) {
     if ((mcb.state != RFC_MX_STATE_IDLE) && (mcb.bd_addr == bd_addr)) {
       /* Multiplexer channel found do not change anything */
-      log::verbose("found, bd_addr:{}, rfc_mcb:{}, lcid:0x{:x}", bd_addr, fmt::ptr(&mcb), mcb.lcid);
+      log::verbose("found, bd_addr:{}, rfc_mcb:{}, lcid:0x{:x}", bd_addr, std::format_ptr(&mcb),
+                   mcb.lcid);
       return &mcb;
     }
   }
@@ -307,14 +308,15 @@
   }
 
   if (dlci > RFCOMM_MAX_DLCI) {
-    log::warn("DLCI {} is too large, bd_addr={}, p_mcb={}", dlci, p_mcb->bd_addr, fmt::ptr(p_mcb));
+    log::warn("DLCI {} is too large, bd_addr={}, p_mcb={}", dlci, p_mcb->bd_addr,
+              std::format_ptr(p_mcb));
     return nullptr;
   }
 
   uint8_t handle = p_mcb->port_handles[dlci];
   if (handle == 0) {
     log::info("Cannot find allocated RFCOMM app port for DLCI {} on {}, p_mcb={}", dlci,
-              p_mcb->bd_addr, fmt::ptr(p_mcb));
+              p_mcb->bd_addr, std::format_ptr(p_mcb));
     return nullptr;
   }
   return &rfc_cb.port.port[handle - 1];
diff --git a/system/stack/rfcomm/rfc_event.h b/system/stack/rfcomm/rfc_event.h
index 64c4e79..f8c7666 100644
--- a/system/stack/rfcomm/rfc_event.h
+++ b/system/stack/rfcomm/rfc_event.h
@@ -124,11 +124,11 @@
   }
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tRFC_EVENT> : enum_formatter<tRFC_EVENT> {};
 template <>
 struct formatter<tRFC_MX_EVENT> : enum_formatter<tRFC_MX_EVENT> {};
 template <>
 struct formatter<tRFC_PORT_EVENT> : enum_formatter<tRFC_PORT_EVENT> {};
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/rfcomm/rfc_l2cap_if.cc b/system/stack/rfcomm/rfc_l2cap_if.cc
index 8b96d26..5b7fc00 100644
--- a/system/stack/rfcomm/rfc_l2cap_if.cc
+++ b/system/stack/rfcomm/rfc_l2cap_if.cc
@@ -268,13 +268,13 @@
   /* If the frame did not pass validation just ignore it */
   if (event == RFC_EVENT_BAD_FRAME) {
     log::warn("Bad RFCOMM frame from lcid=0x{:x}, bd_addr={}, p_mcb={}", lcid, p_mcb->bd_addr,
-              fmt::ptr(p_mcb));
+              std::format_ptr(p_mcb));
     osi_free(p_buf);
     return;
   }
 
   if (rfc_cb.rfc.rx_frame.dlci == RFCOMM_MX_DLCI) {
-    log::verbose("handle multiplexer event {}, p_mcb={}", event, fmt::ptr(p_mcb));
+    log::verbose("handle multiplexer event {}, p_mcb={}", event, std::format_ptr(p_mcb));
     /* Take special care of the Multiplexer Control Messages */
     if (event == RFC_EVENT_UIH) {
       rfc_process_mx_message(p_mcb, p_buf);
@@ -293,11 +293,11 @@
     /* If this is a SABME on new port, check if any app is waiting for it */
     if (event != RFC_EVENT_SABME) {
       log::warn("no for none-SABME event, lcid=0x{:x}, bd_addr={}, p_mcb={}", lcid, p_mcb->bd_addr,
-                fmt::ptr(p_mcb));
+                std::format_ptr(p_mcb));
       if ((p_mcb->is_initiator && !rfc_cb.rfc.rx_frame.cr) ||
           (!p_mcb->is_initiator && rfc_cb.rfc.rx_frame.cr)) {
         log::error("Disconnecting RFCOMM, lcid=0x{:x}, bd_addr={}, p_mcb={}", lcid, p_mcb->bd_addr,
-                   fmt::ptr(p_mcb));
+                   std::format_ptr(p_mcb));
         rfc_send_dm(p_mcb, rfc_cb.rfc.rx_frame.dlci, rfc_cb.rfc.rx_frame.pf);
       }
       osi_free(p_buf);
@@ -309,13 +309,14 @@
       log::error(
               "Disconnecting RFCOMM, no port for dlci {}, lcid=0x{:x}, bd_addr={}, "
               "p_mcb={}",
-              rfc_cb.rfc.rx_frame.dlci, lcid, p_mcb->bd_addr, fmt::ptr(p_mcb));
+              rfc_cb.rfc.rx_frame.dlci, lcid, p_mcb->bd_addr, std::format_ptr(p_mcb));
       rfc_send_dm(p_mcb, rfc_cb.rfc.rx_frame.dlci, true);
       osi_free(p_buf);
       return;
     }
     log::verbose("port_handles[dlci={}]:{}->{}, p_mcb={}", rfc_cb.rfc.rx_frame.dlci,
-                 p_mcb->port_handles[rfc_cb.rfc.rx_frame.dlci], p_port->handle, fmt::ptr(p_mcb));
+                 p_mcb->port_handles[rfc_cb.rfc.rx_frame.dlci], p_port->handle,
+                 std::format_ptr(p_mcb));
     p_mcb->port_handles[rfc_cb.rfc.rx_frame.dlci] = p_port->handle;
     p_port->rfc.p_mcb = p_mcb;
   }
diff --git a/system/stack/rfcomm/rfc_port_fsm.cc b/system/stack/rfcomm/rfc_port_fsm.cc
index 6313abf..0f3ec06 100644
--- a/system/stack/rfcomm/rfc_port_fsm.cc
+++ b/system/stack/rfcomm/rfc_port_fsm.cc
@@ -642,7 +642,8 @@
     if (p_mcb->state != RFC_MX_STATE_DISC_WAIT_UA) {
       PORT_ParNegInd(p_mcb, dlci, p_frame->u.pn.mtu, p_frame->u.pn.conv_layer, p_frame->u.pn.k);
     } else {
-      log::warn("MX PN while disconnecting, bd_addr={}, p_mcb={}", p_mcb->bd_addr, fmt::ptr(p_mcb));
+      log::warn("MX PN while disconnecting, bd_addr={}, p_mcb={}", p_mcb->bd_addr,
+                std::format_ptr(p_mcb));
       rfc_send_dm(p_mcb, dlci, false);
     }
 
@@ -651,7 +652,7 @@
   /* If we are not awaiting response just ignore it */
   tPORT* p_port = port_find_mcb_dlci_port(p_mcb, dlci);
   if ((p_port == nullptr) || !(p_port->rfc.expected_rsp & RFC_RSP_PN)) {
-    log::warn(": Ignore unwanted response, p_mcb={}, bd_addr={}, dlci={}", fmt::ptr(p_mcb),
+    log::warn(": Ignore unwanted response, p_mcb={}, bd_addr={}, dlci={}", std::format_ptr(p_mcb),
               p_mcb->bd_addr, dlci);
     return;
   }
diff --git a/system/stack/rfcomm/rfc_state.h b/system/stack/rfcomm/rfc_state.h
index 32afa29..847c7d0 100644
--- a/system/stack/rfcomm/rfc_state.h
+++ b/system/stack/rfcomm/rfc_state.h
@@ -68,10 +68,10 @@
   }
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tRFC_MX_STATE> : enum_formatter<tRFC_MX_STATE> {};
 template <>
 struct formatter<tRFC_PORT_STATE> : enum_formatter<tRFC_PORT_STATE> {};
 
-}  // namespace fmt
+}  // namespace std
diff --git a/system/stack/rfcomm/rfc_ts_frames.cc b/system/stack/rfcomm/rfc_ts_frames.cc
index ea57abf..e9df718 100644
--- a/system/stack/rfcomm/rfc_ts_frames.cc
+++ b/system/stack/rfcomm/rfc_ts_frames.cc
@@ -683,7 +683,7 @@
   }
 
   if (mx_len != length) {
-    log::error("Bad MX frame, p_mcb={}, bd_addr={}", fmt::ptr(p_mcb), p_mcb->bd_addr);
+    log::error("Bad MX frame, p_mcb={}, bd_addr={}", std::format_ptr(p_mcb), p_mcb->bd_addr);
     osi_free(p_buf);
     return;
   }
@@ -692,7 +692,8 @@
   switch (p_rx_frame->type) {
     case RFCOMM_MX_PN:
       if (length != RFCOMM_MX_PN_LEN) {
-        log::error("Invalid PN length, p_mcb={}, bd_addr={}", fmt::ptr(p_mcb), p_mcb->bd_addr);
+        log::error("Invalid PN length, p_mcb={}, bd_addr={}", std::format_ptr(p_mcb),
+                   p_mcb->bd_addr);
         break;
       }
 
@@ -708,7 +709,7 @@
 
       if (!p_rx_frame->dlci || !RFCOMM_VALID_DLCI(p_rx_frame->dlci) ||
           (p_rx_frame->u.pn.mtu < RFCOMM_MIN_MTU) || (p_rx_frame->u.pn.mtu > RFCOMM_MAX_MTU)) {
-        log::error("Bad PN frame, p_mcb={}, bd_addr={}", fmt::ptr(p_mcb), p_mcb->bd_addr);
+        log::error("Bad PN frame, p_mcb={}, bd_addr={}", std::format_ptr(p_mcb), p_mcb->bd_addr);
         break;
       }
 
diff --git a/system/stack/rfcomm/rfc_utils.cc b/system/stack/rfcomm/rfc_utils.cc
index 2342089..29b8925 100644
--- a/system/stack/rfcomm/rfc_utils.cc
+++ b/system/stack/rfcomm/rfc_utils.cc
@@ -145,7 +145,7 @@
       log::verbose(
               "rfc_alloc_multiplexer_channel:is_initiator:{}, found, state:{}, "
               "p_mcb:{}",
-              is_initiator, rfc_cb.port.rfc_mcb[i].state, fmt::ptr(&rfc_cb.port.rfc_mcb[i]));
+              is_initiator, rfc_cb.port.rfc_mcb[i].state, std::format_ptr(&rfc_cb.port.rfc_mcb[i]));
       return &rfc_cb.port.rfc_mcb[i];
     }
   }
@@ -166,7 +166,7 @@
       log::verbose(
               "rfc_alloc_multiplexer_channel:is_initiator:{}, create new p_mcb:{}, "
               "index:{}",
-              is_initiator, fmt::ptr(&rfc_cb.port.rfc_mcb[j]), j);
+              is_initiator, std::format_ptr(&rfc_cb.port.rfc_mcb[j]), j);
 
       p_mcb->mcb_timer = alarm_new("rfcomm_mcb.mcb_timer");
       p_mcb->cmd_q = fixed_queue_new(SIZE_MAX);
@@ -416,8 +416,9 @@
   /* if passed a buffer queue it */
   if (p_buf != NULL) {
     if (p_mcb->cmd_q == NULL) {
-      log::error("empty queue: p_mcb = {} p_mcb->lcid = {} cached p_mcb = {}", fmt::ptr(p_mcb),
-                 p_mcb->lcid, fmt::ptr(rfc_find_lcid_mcb(p_mcb->lcid)));
+      log::error("empty queue: p_mcb = {} p_mcb->lcid = {} cached p_mcb = {}",
+                 std::format_ptr(p_mcb), p_mcb->lcid,
+                 std::format_ptr(rfc_find_lcid_mcb(p_mcb->lcid)));
     }
     fixed_queue_enqueue(p_mcb->cmd_q, p_buf);
   }
diff --git a/system/stack/sdp/sdp_api.cc b/system/stack/sdp/sdp_api.cc
index 589a7eb..093dddd 100644
--- a/system/stack/sdp/sdp_api.cc
+++ b/system/stack/sdp/sdp_api.cc
@@ -81,7 +81,7 @@
   if (p_db == NULL || (sizeof(tSDP_DISCOVERY_DB) > len) || num_attr > SDP_MAX_ATTR_FILTERS ||
       num_uuid > SDP_MAX_UUID_FILTERS) {
     log::error("SDP_InitDiscoveryDb Illegal param: p_db {}, len {}, num_uuid {}, num_attr {}",
-               fmt::ptr(p_db), len, num_uuid, num_attr);
+               std::format_ptr(p_db), len, num_uuid, num_attr);
 
     return false;
   }
@@ -1124,7 +1124,7 @@
   if (conn_cb.device_address == RawAddress::kEmpty) {
     return;
   }
-  LOG_DUMPSYS(fd, "peer:%s discovery_state:%s", fmt::format("{}", conn_cb.device_address).c_str(),
+  LOG_DUMPSYS(fd, "peer:%s discovery_state:%s", std::format("{}", conn_cb.device_address).c_str(),
               sdp_disc_wait_text(conn_cb.disc_state).c_str());
   LOG_DUMPSYS(fd, "  connection_state:%s connection_flags:0x%02x mtu:%hu l2cap_cid:%hu",
               sdp_state_text(conn_cb.con_state).c_str(), conn_cb.con_flags, conn_cb.rem_mtu_size,
diff --git a/system/stack/sdp/sdp_db.cc b/system/stack/sdp/sdp_db.cc
index dbb8ba7..8ecdf9e 100644
--- a/system/stack/sdp/sdp_db.cc
+++ b/system/stack/sdp/sdp_db.cc
@@ -288,22 +288,22 @@
         snprintf(&num_array[i * 2], sizeof(num_array) - i * 2, "%02X", (uint8_t)(p_val[i]));
       }
       log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, p_val:{}, *p_val:{}",
-                   handle, attr_id, attr_type, attr_len, fmt::ptr(p_val), num_array);
+                   handle, attr_id, attr_type, attr_len, std::format_ptr(p_val), num_array);
     } else if (attr_type == BOOLEAN_DESC_TYPE) {
       log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, p_val:{}, *p_val:{}",
-                   handle, attr_id, attr_type, attr_len, fmt::ptr(p_val), *p_val);
+                   handle, attr_id, attr_type, attr_len, std::format_ptr(p_val), *p_val);
     } else if ((attr_type == TEXT_STR_DESC_TYPE) || (attr_type == URL_DESC_TYPE)) {
       if (p_val[attr_len - 1] == '\0') {
         log::verbose(
                 "SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, p_val:{}, *p_val:{}",
-                handle, attr_id, attr_type, attr_len, fmt::ptr(p_val), (char*)p_val);
+                handle, attr_id, attr_type, attr_len, std::format_ptr(p_val), (char*)p_val);
       } else {
         log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, p_val:{}", handle,
-                     attr_id, attr_type, attr_len, fmt::ptr(p_val));
+                     attr_id, attr_type, attr_len, std::format_ptr(p_val));
       }
     } else {
       log::verbose("SDP_AddAttribute: handle:{:X}, id:{:04X}, type:{}, len:{}, p_val:{}", handle,
-                   attr_id, attr_type, attr_len, fmt::ptr(p_val));
+                   attr_id, attr_type, attr_len, std::format_ptr(p_val));
     }
   }
 
diff --git a/system/stack/smp/smp_int.h b/system/stack/smp/smp_int.h
index dd51c0d..7725bd7 100644
--- a/system/stack/smp/smp_int.h
+++ b/system/stack/smp/smp_int.h
@@ -502,13 +502,13 @@
 void smp_clear_local_oob_data();
 bool smp_has_local_oob_data();
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tSMP_EVENT> : enum_formatter<tSMP_EVENT> {};
 template <>
 struct formatter<tSMP_OPCODE> : enum_formatter<tSMP_OPCODE> {};
 template <>
 struct formatter<tSMP_ASSO_MODEL> : enum_formatter<tSMP_ASSO_MODEL> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif /* SMP_INT_H */
diff --git a/system/test/headless/headless.cc b/system/test/headless/headless.cc
index 5f0d820..f115ddd 100644
--- a/system/test/headless/headless.cc
+++ b/system/test/headless/headless.cc
@@ -84,7 +84,7 @@
     }
   }
   log::info("num_callbacks:{} status:{} num_properties:{} properties:{}", num_callbacks,
-            bt_status_text(status), num_properties, fmt::ptr(properties));
+            bt_status_text(status), num_properties, std::format_ptr(properties));
 }
 
 void remote_device_properties(bt_status_t status, RawAddress* bd_addr, int num_properties,
@@ -100,7 +100,7 @@
     }
   }
   log::info("num_callbacks:{} status:{} device:{} num_properties:{} properties:{}", num_callbacks,
-            bt_status_text(status), STR(*bd_addr), num_properties, fmt::ptr(properties));
+            bt_status_text(status), STR(*bd_addr), num_properties, std::format_ptr(properties));
 }
 
 // Aggregate disparate variables from callback API into unified single structure
@@ -114,7 +114,7 @@
     }
   }
   log::info("Device found callback: num_properties:{} properties:{}", num_properties,
-            fmt::ptr(properties));
+            std::format_ptr(properties));
 }
 
 void discovery_state_changed(bt_discovery_state_t state) {
diff --git a/system/test/headless/interface.h b/system/test/headless/interface.h
index 11c314c..47e5484 100644
--- a/system/test/headless/interface.h
+++ b/system/test/headless/interface.h
@@ -98,7 +98,8 @@
       : callback_params_t(name, callback_type) {
     for (int i = 0; i < num_properties; i++) {
       log::debug("Processing property {}/{} {} type:{} val:{}", i, num_properties,
-                 fmt::ptr(&properties[i]), properties[i].type, fmt::ptr(properties[i].val));
+                 std::format_ptr(&properties[i]), properties[i].type,
+                 std::format_ptr(properties[i].val));
       property_queue_.push_back(bluetooth::test::headless::property_factory(properties[i]));
     }
   }
@@ -132,7 +133,7 @@
   uint16_t acl_handle;
 
   std::string ToString() const override {
-    return fmt::format(
+    return std::format(
             "status:{} remote_bd_addr:{} state:{} transport:{} reason:{}"
             " direction:{} handle:{}",
             bt_status_text(status), remote_bd_addr.ToString(),
@@ -153,7 +154,7 @@
 
   bt_discovery_state_t state;
   std::string ToString() const override {
-    return fmt::format("state:{}", bt_discovery_state_text(state));
+    return std::format("state:{}", bt_discovery_state_text(state));
   }
 };
 
@@ -168,7 +169,7 @@
   bt_status_t status;
 
   std::string ToString() const override {
-    return fmt::format("status:{} num_properties:{}", bt_status_text(status), num_properties());
+    return std::format("status:{} num_properties:{}", bt_status_text(status), num_properties());
   }
 };
 
@@ -187,7 +188,7 @@
   RawAddress bd_addr;
 
   std::string ToString() const override {
-    return fmt::format("status:{} bd_addr:{} num_properties:{}", bt_status_text(status),
+    return std::format("status:{} bd_addr:{} num_properties:{}", bt_status_text(status),
                        bd_addr.ToString(), num_properties());
   }
 };
@@ -201,7 +202,7 @@
   virtual ~device_found_params_t() {}
 
   std::string ToString() const override {
-    return fmt::format("num_properties:{}", num_properties());
+    return std::format("num_properties:{}", num_properties());
   }
 };
 
diff --git a/system/test/headless/property.h b/system/test/headless/property.h
index 8227979..6af17a2 100644
--- a/system/test/headless/property.h
+++ b/system/test/headless/property.h
@@ -99,7 +99,7 @@
 
 public:
   virtual std::string ToString() const override {
-    return fmt::format("Unimplemented property type:{} name:{}", type, bt_property_type_text(type));
+    return std::format("Unimplemented property type:{} name:{}", type, bt_property_type_text(type));
   }
 };
 
@@ -119,7 +119,7 @@
   }
 
   virtual std::string ToString() const override {
-    return fmt::format("Number of uuids:{}", get_uuids().size());
+    return std::format("Number of uuids:{}", get_uuids().size());
   }
 
 private:
@@ -136,7 +136,7 @@
     return std::string(s);
   }
 
-  virtual std::string ToString() const override { return fmt::format("Name:{}", get_name()); }
+  virtual std::string ToString() const override { return std::format("Name:{}", get_name()); }
 };
 
 struct bdaddr_t : public bt_property_t {
@@ -153,7 +153,7 @@
   }
 
   virtual std::string ToString() const override {
-    return fmt::format("bd_addr:{}", get_addr().ToString());
+    return std::format("bd_addr:{}", get_addr().ToString());
   }
 };
 
@@ -168,7 +168,7 @@
   }
 
   virtual std::string ToString() const override {
-    return fmt::format("cod:0x{:04x}", get_class_of_device());
+    return std::format("cod:0x{:04x}", get_class_of_device());
   }
 };
 
@@ -183,7 +183,7 @@
   }
 
   virtual std::string ToString() const override {
-    return fmt::format("tod:0x{:04x}", get_type_of_device());
+    return std::format("tod:0x{:04x}", get_type_of_device());
   }
 };
 
diff --git a/system/test/headless/stopwatch.h b/system/test/headless/stopwatch.h
index e2b549c..d7f5e36 100644
--- a/system/test/headless/stopwatch.h
+++ b/system/test/headless/stopwatch.h
@@ -40,7 +40,7 @@
   std::string ToString() { return ToString(""); }
 
   std::string ToString(const std::string& comment) {
-    return fmt::format("{}: {} ms {}", name_, static_cast<unsigned long>(LapMs()), comment);
+    return std::format("{}: {} ms {}", name_, static_cast<unsigned long>(LapMs()), comment);
   }
 
 private:
diff --git a/system/test/headless/utils/power_mode_client.h b/system/test/headless/utils/power_mode_client.h
index 1f66c63..d27c59e 100644
--- a/system/test/headless/utils/power_mode_client.h
+++ b/system/test/headless/utils/power_mode_client.h
@@ -66,7 +66,7 @@
   tHCI_STATUS hci_status;
 
   std::string ToString() const {
-    return fmt::format("bd_addr:{} pm_status:{} value:{} hci_status:{}", bd_addr.ToString(),
+    return std::format("bd_addr:{} pm_status:{} value:{} hci_status:{}", bd_addr.ToString(),
                        power_mode_status_text(status), value, hci_status_code_text(hci_status));
   }
 };
diff --git a/system/test/stub/osi.cc b/system/test/stub/osi.cc
index 17accc1..c087497 100644
--- a/system/test/stub/osi.cc
+++ b/system/test/stub/osi.cc
@@ -346,18 +346,18 @@
 bool alarm_is_scheduled(const alarm_t* alarm) {
   inc_func_call_count(__func__);
 
-  auto iter =
-          find_if(previous_fake_osi_alarms_.begin(), previous_fake_osi_alarms_.end(),
-                  [alarm](auto const& a) {
-                    bluetooth::log::debug("iter: {} == {} ?", fmt::ptr(a.alarm), fmt::ptr(alarm));
-                    return a.alarm == alarm;
-                  });
+  auto iter = find_if(previous_fake_osi_alarms_.begin(), previous_fake_osi_alarms_.end(),
+                      [alarm](auto const& a) {
+                        bluetooth::log::debug("iter: {} == {} ?", std::format_ptr(a.alarm),
+                                              std::format_ptr(alarm));
+                        return a.alarm == alarm;
+                      });
   if (iter != previous_fake_osi_alarms_.end()) {
     return true;
   }
 
-  bluetooth::log::debug(" {} == {} ?", fmt::ptr(fake_osi_alarm_set_on_mloop_.alarm),
-                        fmt::ptr(alarm));
+  bluetooth::log::debug(" {} == {} ?", std::format_ptr(fake_osi_alarm_set_on_mloop_.alarm),
+                        std::format_ptr(alarm));
 
   return fake_osi_alarm_set_on_mloop_.alarm == alarm;
 }
@@ -371,14 +371,14 @@
     auto iter = find_if(previous_fake_osi_alarms_.begin(), previous_fake_osi_alarms_.end(),
                         [alarm](auto const& a) { return a.alarm == alarm; });
     if (iter != previous_fake_osi_alarms_.end()) {
-      bluetooth::log::debug(" clearing alarm {} ", fmt::ptr(iter->alarm));
+      bluetooth::log::debug(" clearing alarm {} ", std::format_ptr(iter->alarm));
       previous_fake_osi_alarms_.erase(iter);
       return;
     }
   }
 
   if (fake_osi_alarm_set_on_mloop_.alarm == alarm || alarm == nullptr) {
-    bluetooth::log::debug(" clearing alarm {} ", fmt::ptr(alarm));
+    bluetooth::log::debug(" clearing alarm {} ", std::format_ptr(alarm));
     fake_osi_alarm_set_on_mloop_.alarm = nullptr;
     fake_osi_alarm_set_on_mloop_.interval_ms = 0;
     fake_osi_alarm_set_on_mloop_.cb = nullptr;
@@ -412,11 +412,11 @@
   inc_func_call_count(__func__);
 
   if (fake_osi_alarm_set_on_mloop_.alarm != nullptr) {
-    bluetooth::log::info("Queuing alarm {}", fmt::ptr(fake_osi_alarm_set_on_mloop_.alarm));
+    bluetooth::log::info("Queuing alarm {}", std::format_ptr(fake_osi_alarm_set_on_mloop_.alarm));
     previous_fake_osi_alarms_.push_back(fake_osi_alarm_set_on_mloop_);
   }
 
-  bluetooth::log::info("Adding alarm {}", fmt::ptr(alarm));
+  bluetooth::log::info("Adding alarm {}", std::format_ptr(alarm));
   fake_osi_alarm_set_on_mloop_.alarm = alarm;
   fake_osi_alarm_set_on_mloop_.interval_ms = interval_ms;
   fake_osi_alarm_set_on_mloop_.cb = cb;
diff --git a/system/types/ble_address_with_type.h b/system/types/ble_address_with_type.h
index 1c9869a..18f7cd0 100644
--- a/system/types/ble_address_with_type.h
+++ b/system/types/ble_address_with_type.h
@@ -170,13 +170,13 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBLE_BD_ADDR> : formatter<std::string> {
   template <class Context>
   typename Context::iterator format(const tBLE_BD_ADDR& address, Context& ctx) const {
     std::string repr = address.ToRedactedStringForLogging();
-    return fmt::formatter<std::string>::format(repr, ctx);
+    return std::formatter<std::string>::format(repr, ctx);
   }
 };
 template <>
@@ -184,10 +184,10 @@
   template <class Context>
   typename Context::iterator format(const tAclLinkSpec& address, Context& ctx) const {
     std::string repr = address.ToRedactedStringForLogging();
-    return fmt::formatter<std::string>::format(repr, ctx);
+    return std::formatter<std::string>::format(repr, ctx);
   }
 };
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>
 
diff --git a/system/types/bluetooth/uuid.h b/system/types/bluetooth/uuid.h
index 9c09441..eab8bb1 100644
--- a/system/types/bluetooth/uuid.h
+++ b/system/types/bluetooth/uuid.h
@@ -150,9 +150,9 @@
 
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<bluetooth::Uuid> : ostream_formatter {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
diff --git a/system/types/bt_transport.h b/system/types/bt_transport.h
index 403e85d..a787a57 100644
--- a/system/types/bt_transport.h
+++ b/system/types/bt_transport.h
@@ -40,9 +40,9 @@
   RETURN_UNKNOWN_TYPE_STRING(tBT_TRANSPORT, transport);
 }
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tBT_TRANSPORT> : enum_formatter<tBT_TRANSPORT> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif
diff --git a/system/types/hci_role.h b/system/types/hci_role.h
index bbedb23..0a68582 100644
--- a/system/types/hci_role.h
+++ b/system/types/hci_role.h
@@ -53,9 +53,9 @@
 
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<tHCI_ROLE> : enum_formatter<tHCI_ROLE> {};
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
diff --git a/system/types/raw_address.h b/system/types/raw_address.h
index 60ff23f..2a8540b 100644
--- a/system/types/raw_address.h
+++ b/system/types/raw_address.h
@@ -113,15 +113,15 @@
 #if __has_include(<bluetooth/log.h>)
 #include <bluetooth/log.h>
 
-namespace fmt {
+namespace std {
 template <>
 struct formatter<RawAddress> : formatter<std::string> {
   template <class Context>
   typename Context::iterator format(const RawAddress& address, Context& ctx) const {
     std::string repr = address.ToRedactedStringForLogging();
-    return fmt::formatter<std::string>::format(repr, ctx);
+    return std::formatter<std::string>::format(repr, ctx);
   }
 };
-}  // namespace fmt
+}  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)