Merge "Create an Android Lint rule to prevent creation of log enforcement vars" into main
diff --git a/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java b/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java
index 6020043..efe155f 100644
--- a/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java
+++ b/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java
@@ -26,7 +26,6 @@
 import com.android.bluetooth.R;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 class Util {
@@ -118,15 +117,15 @@
      */
     public static List<Metadata> toMetadataList(Context context,
             List<MediaSession.QueueItem> items) {
+        ArrayList<Metadata> list = new ArrayList<>();
 
-        if (items == null) return Collections.emptyList();
+        if (items == null) return list;
 
-        ArrayList<Metadata> list = new ArrayList<Metadata>();
         for (int i = 0; i < items.size(); i++) {
             Metadata data = toMetadata(context, items.get(i));
             if (isEmptyData(data)) {
                 Log.e(TAG, "Received an empty Metadata item in list. Returning an empty queue");
-                return Collections.emptyList();
+                return new ArrayList<>();
             }
             data.trackNum = "" + (i + 1);
             data.numTracks = "" + items.size();
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
index b835d91..2f6ba51 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -636,7 +636,7 @@
             return;
         }
 
-        boolean isAssistantActive = isAnyReceiverReceivingBroadcast();
+        boolean isAssistantActive = isAnyReceiverReceivingBroadcast(getConnectedDevices());
 
         if (isAssistantActive) {
             /* Assistant become active */
@@ -1908,25 +1908,6 @@
         return true;
     }
 
-    private boolean isAnyReceiverReceivingBroadcast() {
-        for (BluetoothDevice device : getConnectedDevices()) {
-            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
-                for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
-                    Long syncState = receiveState.getBisSyncState().get(i);
-
-                    /* Not synced to BIS of failed to sync to BIG */
-                    if (syncState == 0x00000000 || syncState == 0xFFFFFFFF) {
-                        continue;
-                    }
-
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
     /** Return true if there is any non primary device receiving broadcast */
     private boolean isAudioSharingModeOn(Integer broadcastId) {
         if (mLocalBroadcastReceivers == null) {
@@ -2069,7 +2050,7 @@
         mUnicastSourceStreamStatus = Optional.of(status);
 
         if (status == STATUS_LOCAL_STREAM_REQUESTED) {
-            if (isAnyReceiverReceivingBroadcast()) {
+            if (isAnyReceiverReceivingBroadcast(getConnectedDevices())) {
                 suspendAllReceiversSourceSynchronization();
             }
         } else if (status == STATUS_LOCAL_STREAM_SUSPENDED) {
@@ -2092,6 +2073,25 @@
         }
     }
 
+    /** Check if any sink receivers are receiving broadcast stream */
+    public boolean isAnyReceiverReceivingBroadcast(List<BluetoothDevice> devices) {
+        for (BluetoothDevice device : devices) {
+            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
+                for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
+                    Long syncState = receiveState.getBisSyncState().get(i);
+                    /* Not synced to BIS of failed to sync to BIG */
+                    if (syncState == 0x00000000 || syncState == 0xFFFFFFFF) {
+                        continue;
+                    }
+
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
     /** Handle broadcast state changed */
     public void notifyBroadcastStateChanged(int state, int broadcastId) {
         switch (state) {
diff --git a/android/app/src/com/android/bluetooth/hid/HidHostService.java b/android/app/src/com/android/bluetooth/hid/HidHostService.java
index 48b791e..8cfd66d 100644
--- a/android/app/src/com/android/bluetooth/hid/HidHostService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidHostService.java
@@ -655,12 +655,11 @@
                     Log.e(
                             TAG,
                             "handleMessageConnectStateChanged: "
-                                    + "remove unknown device: "
+                                    + "disconnect and disable the unknown device: "
                                     + device
                                     + " state: "
                                     + state);
-                    mNativeInterface.virtualUnPlug(
-                            getByteAddress(device), getAddressType(device), getTransport(device));
+                    nativeDisconnect(device, transport, false);
                     return;
                 }
             }
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 63bacb2..a96b53f 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -2196,7 +2196,9 @@
             case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP:
                 if (Flags.leaudioUnicastInactivateDeviceBasedOnContext()) {
                     LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId);
-                    if (groupDescriptor != null && groupDescriptor.mIsActive) {
+                    if (groupDescriptor != null
+                            && groupDescriptor.mIsActive
+                            && !isGroupReceivingBroadcast(groupId)) {
                         Log.i(
                                 TAG,
                                 "Group "
@@ -2332,6 +2334,19 @@
         }
     }
 
+    private boolean isGroupReceivingBroadcast(int groupId) {
+        if (!Flags.leaudioBroadcastAudioHandoverPolicies()) {
+            return false;
+        }
+
+        BassClientService bassClientService = getBassClientService();
+        if (bassClientService == null) {
+            return false;
+        }
+
+        return bassClientService.isAnyReceiverReceivingBroadcast(getGroupDevices(groupId));
+    }
+
     private void notifyGroupStreamStatusChanged(int groupId, int groupStreamStatus) {
         if (mLeAudioCallbacks != null) {
             int n = mLeAudioCallbacks.beginBroadcast();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
index a4b11d3..263d911 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
@@ -1588,6 +1588,99 @@
         }
     }
 
+    @Test
+    public void testIsAnyReceiverReceivingBroadcast() {
+        prepareConnectedDeviceGroup();
+        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
+        verifyAddSourceForGroup(meta);
+        for (BassClientStateMachine sm : mStateMachines.values()) {
+            if (sm.getDevice().equals(mCurrentDevice)) {
+                injectRemoteSourceStateSourceAdded(
+                        sm,
+                        meta,
+                        TEST_SOURCE_ID,
+                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
+                        meta.isEncrypted()
+                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+                                : BluetoothLeBroadcastReceiveState
+                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+                        null);
+                injectRemoteSourceStateChanged(
+                        sm,
+                        meta,
+                        TEST_SOURCE_ID,
+                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+                        meta.isEncrypted()
+                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+                                : BluetoothLeBroadcastReceiveState
+                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+                        null,
+                        (long) 0x00000000);
+            } else if (sm.getDevice().equals(mCurrentDevice1)) {
+                injectRemoteSourceStateSourceAdded(
+                        sm,
+                        meta,
+                        TEST_SOURCE_ID + 1,
+                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
+                        meta.isEncrypted()
+                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+                                : BluetoothLeBroadcastReceiveState
+                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+                        null);
+                injectRemoteSourceStateChanged(
+                        sm,
+                        meta,
+                        TEST_SOURCE_ID + 1,
+                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+                        meta.isEncrypted()
+                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+                                : BluetoothLeBroadcastReceiveState
+                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+                        null,
+                        (long) 0x00000000);
+            }
+        }
+
+        List<BluetoothDevice> devices = mBassClientService.getConnectedDevices();
+        // Verify isAnyReceiverReceivingBroadcast returns false if no BIS synced
+        assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(devices)).isFalse();
+
+        // Update receiver state with lost BIS sync
+        for (BassClientStateMachine sm : mStateMachines.values()) {
+            if (sm.getDevice().equals(mCurrentDevice)) {
+                injectRemoteSourceStateChanged(
+                        sm,
+                        meta,
+                        TEST_SOURCE_ID,
+                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+                        meta.isEncrypted()
+                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+                                : BluetoothLeBroadcastReceiveState
+                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+                        null,
+                        (long) 0x00000001);
+            } else if (sm.getDevice().equals(mCurrentDevice1)) {
+                injectRemoteSourceStateChanged(
+                        sm,
+                        meta,
+                        TEST_SOURCE_ID + 1,
+                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+                        meta.isEncrypted()
+                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+                                : BluetoothLeBroadcastReceiveState
+                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+                        null,
+                        (long) 0x00000002);
+            }
+        }
+        BluetoothDevice invalidDevice = TestUtils.getTestDevice(mBluetoothAdapter, 2);
+        // Verify isAnyReceiverReceivingBroadcast returns false if invalid device
+        assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(List.of(invalidDevice)))
+                .isFalse();
+        // Verify isAnyReceiverReceivingBroadcast returns true if BIS synced
+        assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(devices)).isTrue();
+    }
+
     private void prepareTwoSynchronizedDevices() {
         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
index fc08492..6fa86d0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
@@ -17,6 +17,7 @@
 package com.android.bluetooth.hfpclient;
 
 import static android.content.pm.PackageManager.FEATURE_WATCH;
+import java.util.concurrent.TimeUnit;
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
@@ -65,6 +66,7 @@
     private boolean mIsHeadsetClientServiceStarted;
 
     private static final int STANDARD_WAIT_MILLIS = 1000;
+    private static final int SERVICE_START_WAIT_MILLIS = 100;
 
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
@@ -127,6 +129,8 @@
     public void testUpdateBatteryLevel() throws Exception {
         startService();
 
+        // Adding a wait to prevent potential failure caused by delayed broadcast intent.
+        TimeUnit.MILLISECONDS.sleep(SERVICE_START_WAIT_MILLIS);
         // Put mock state machine
         BluetoothDevice device =
                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
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 9ae8494..8b8b100 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
@@ -58,6 +58,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.bluetooth.TestUtils;
+import com.android.bluetooth.bass_client.BassClientService;
 import com.android.bluetooth.btservice.ActiveDeviceManager;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ServiceFactory;
@@ -132,6 +133,7 @@
     @Mock private VolumeControlService mVolumeControlService;
     @Mock private HapClientService mHapClientService;
     @Mock private CsipSetCoordinatorService mCsipSetCoordinatorService;
+    @Mock private BassClientService mBassClientService;
     @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
     @Spy private ServiceFactory mServiceFactory = new ServiceFactory();
 
@@ -202,10 +204,12 @@
         mService.mMcpService = mMcpService;
         mService.mTbsService = mTbsService;
         mService.mHapClientService = mHapClientService;
+        mService.mBassClientService = mBassClientService;
         mService.mServiceFactory = mServiceFactory;
         when(mServiceFactory.getVolumeControlService()).thenReturn(mVolumeControlService);
         when(mServiceFactory.getHapClientService()).thenReturn(mHapClientService);
         when(mServiceFactory.getCsipSetCoordinatorService()).thenReturn(mCsipSetCoordinatorService);
+        when(mServiceFactory.getBassClientService()).thenReturn(mBassClientService);
 
         LeAudioStackEvent stackEvent =
         new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
@@ -1767,6 +1771,50 @@
                         eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class));
     }
 
+    @Test
+    public void testMediaContextUnavailableWhileReceivingBroadcast() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT);
+        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
+        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES);
+
+        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+        connectTestDevice(mSingleDevice, testGroupId);
+
+        Integer contexts = BluetoothLeAudio.CONTEXT_TYPE_MEDIA;
+        injectAudioConfChanged(testGroupId, contexts, 1);
+
+        // Set group and device as active.
+        injectGroupStatusChange(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
+
+        verify(mAudioManager, times(1))
+                .handleBluetoothActiveDeviceChanged(
+                        eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class));
+
+        doReturn(true)
+                .when(mBassClientService)
+                .isAnyReceiverReceivingBroadcast(mService.getGroupDevices(testGroupId));
+        LeAudioStackEvent healthBasedGroupAction =
+                new LeAudioStackEvent(
+                        LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION);
+        healthBasedGroupAction.valueInt1 = testGroupId;
+        healthBasedGroupAction.valueInt2 =
+                LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP;
+        mService.messageFromNative(healthBasedGroupAction);
+        // Verify skip setting device inactive if group is receiving broadcast
+        verify(mAudioManager, times(0))
+                .handleBluetoothActiveDeviceChanged(
+                        eq(null), any(), any(BluetoothProfileConnectionInfo.class));
+
+        doReturn(false)
+                .when(mBassClientService)
+                .isAnyReceiverReceivingBroadcast(mService.getGroupDevices(testGroupId));
+        mService.messageFromNative(healthBasedGroupAction);
+        // Verify setting device inactive if group is not receiving broadcast
+        verify(mAudioManager, times(1))
+                .handleBluetoothActiveDeviceChanged(
+                        eq(null), any(), any(BluetoothProfileConnectionInfo.class));
+    }
+
     private void sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus) {
 
         onGroupStatusCallbackCalled = false;
diff --git a/android/pandora/server/configs/PtsBotTest.xml b/android/pandora/server/configs/PtsBotTest.xml
index af62689..938be7d 100644
--- a/android/pandora/server/configs/PtsBotTest.xml
+++ b/android/pandora/server/configs/PtsBotTest.xml
@@ -27,6 +27,11 @@
         <option name="host-background-command" value="adb -s $SERIAL shell am instrument --no-hidden-api-checks -w com.android.pandora/.Main" />
     </target_preparer>
 
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="setprop persist.bluetooth.btsnooplogmode full" />
+        <option name="run-command" value="setprop log.tag.bluetooth VERBOSE" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.PythonVirtualenvPreparer">
         <option name="dep-module" value="grpcio" />
         <option name="dep-module" value="protobuf==3.20.1" />
diff --git a/flags/gap.aconfig b/flags/gap.aconfig
index b40e1d7..455a50f 100644
--- a/flags/gap.aconfig
+++ b/flags/gap.aconfig
@@ -163,3 +163,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "scan_record_manufacturer_data_merge"
+    namespace: "bluetooth"
+    description: "If a scan record has multiple datas under same manufacturer id, merge the values"
+    bug: "331723396"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/hfp.aconfig b/flags/hfp.aconfig
index b50bb4a..0b8fb02 100644
--- a/flags/hfp.aconfig
+++ b/flags/hfp.aconfig
@@ -56,3 +56,13 @@
     description: "during ongoing call, pretend network service in +CIND when network is unavailable"
     bug: "317307596"
 }
+
+flag {
+    name: "bta_ag_cmd_brsf_allow_uint32"
+    namespace: "bluetooth"
+    description: "Allow and ignore the RFU bits set to 0b1 rather than responding ERROR"
+    bug: "331129986"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/leaudio.aconfig b/flags/leaudio.aconfig
index be755c8..90052a3 100644
--- a/flags/leaudio.aconfig
+++ b/flags/leaudio.aconfig
@@ -222,3 +222,23 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "leaudio_quick_leaudio_toggle_switch_fix"
+    namespace: "bluetooth"
+    description: "Fix for one device being not connected when toggle LeAudio is switch during bonding"
+    bug: "328595942"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "leaudio_broadcast_assistant_handle_command_statuses"
+    namespace: "bluetooth"
+    description: "Handle command statuses to recover from errors"
+    bug: "331167629"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/floss/pandora/floss/audio_test_data.py b/floss/pandora/floss/audio_test_data.py
new file mode 100644
index 0000000..e2cf269
--- /dev/null
+++ b/floss/pandora/floss/audio_test_data.py
@@ -0,0 +1,50 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#     https://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.
+"""This module provides audio test data."""
+
+import os
+
+
+class AudioTestDataException(Exception):
+    """Exception for audio test data."""
+    pass
+
+
+class AudioTestData(object):
+    """Class to represent audio test data."""
+
+    def __init__(self, data_format=None, path=None, frequencies=None, duration_secs=None):
+        """Initializes an audio test file.
+
+        Args:
+            data_format: A dict containing data format including
+                         file_type, sample_format, channel, and rate.
+                         file_type: file type e.g. 'raw' or 'wav'.
+                         sample_format: One of the keys in audio_utils.SAMPLE_FORMAT.
+                         channel: number of channels.
+                         rate: sampling rate.
+            path: The path to the file.
+            frequencies: A list containing the frequency of each channel in this file.
+                         Only applicable to data of sine tone.
+            duration_secs: Duration of test file in seconds.
+
+        Raises:
+            AudioTestDataException if the path does not exist.
+        """
+        self.data_format = data_format
+        if not os.path.exists(path):
+            raise AudioTestDataException('Can not find path %s' % path)
+        self.path = path
+        self.frequencies = frequencies
+        self.duration_secs = duration_secs
diff --git a/floss/pandora/floss/audio_utils.py b/floss/pandora/floss/audio_utils.py
new file mode 100644
index 0000000..98e1231
--- /dev/null
+++ b/floss/pandora/floss/audio_utils.py
@@ -0,0 +1,187 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#     https://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.
+
+import logging
+import os
+import subprocess
+import wave
+
+from floss.pandora.floss import audio_test_data
+from floss.pandora.floss import cras_utils
+from floss.pandora.floss import sox_utils
+from floss.pandora.floss import utils
+
+CRAS_BLUETOOTH_OUTPUT_NODE_TYPE = 'BLUETOOTH'
+
+AUDIO_TEST_DIR = '/tmp/audio'
+
+A2DP_TEST_DATA = {
+    'rate': 48000,
+    'channels': 2,
+    'frequencies': (440, 20000),
+    'file': os.path.join(AUDIO_TEST_DIR, 'binaural_sine_440hz_20000hz_rate48000_5secs.wav'),
+    'recorded_by_sink': os.path.join(AUDIO_TEST_DIR, 'a2dp_recorded_by_sink.wav'),
+    'chunk_in_secs': 5,
+    'bit_width': 16,
+    'format': 'S16_LE',
+    'duration': 5,
+}
+
+A2DP_PLAYBACK_DATA = {
+    'rate': 44100,
+    'channels': 2,
+    'file': os.path.join(AUDIO_TEST_DIR, 'audio_playback.wav'),
+    'sample_width': 2
+}
+
+SAMPLE_FORMATS = dict(S32_LE=dict(message='Signed 32-bit integer, little-endian', dtype_str='<i', size_bytes=4),
+                      S16_LE=dict(message='Signed 16-bit integer, little-endian', dtype_str='<i', size_bytes=2))
+
+
+@utils.dbus_safe(None)
+def get_selected_output_device_type():
+    """Gets the selected audio output node type.
+
+    Returns:
+        The node type of the selected output device.
+    """
+    return str(cras_utils.get_selected_output_device_type())
+
+
+@utils.dbus_safe(None)
+def select_output_node(node_type):
+    """Selects the audio output node.
+
+    Args:
+        node_type: The node type of the Bluetooth peer device.
+
+    Returns:
+        True if the operation succeeds.
+    """
+    return cras_utils.set_single_selected_output_node(node_type)
+
+
+def select_audio_output_node():
+    """Selects the audio output node through cras."""
+
+    def bluetooth_type_selected(node_type):
+        """Checks if the bluetooth node type is selected."""
+        selected = get_selected_output_device_type()
+        logging.debug('active output node type: %s, expected %s', selected, node_type)
+        return selected == node_type
+
+    node_type = CRAS_BLUETOOTH_OUTPUT_NODE_TYPE
+    if not select_output_node(node_type):
+        return False
+
+    desc = 'waiting for %s as active cras audio output node type' % node_type
+    try:
+        utils.poll_for_condition(condition=lambda: bluetooth_type_selected(node_type),
+                                 timeout=20,
+                                 sleep_interval=1,
+                                 desc=desc)
+    except TimeoutError:
+        return False
+    return True
+
+
+def generate_audio_test_data(path, data_format=None, frequencies=None, duration_secs=None, volume_scale=None):
+    """Generates audio test data with specified format and frequencies.
+
+    Args:
+        path: The path to the file.
+        data_format: A dict containing data format including
+                     file_type, sample_format, channel, and rate.
+                     file_type: file type e.g. 'raw' or 'wav'.
+                     sample_format: One of the keys in audio_data.SAMPLE_FORMAT.
+                     channel: number of channels.
+                     ate: sampling rate.
+        frequencies: A list containing the frequency of each channel in this file.
+                     Only applicable to data of sine tone.
+        duration_secs: Duration of test file in seconds.
+        volume_scale: A float for volume scale used in sox command.
+                      E.g. 0.5 to scale volume by half. -1.0 to invert.
+
+    Returns:
+        An AudioTestData object.
+    """
+    sox_file_path = path
+
+    if data_format is None:
+        data_format = dict(file_type='wav', sample_format='S16_LE', channel=2, rate=48000)
+
+    sample_format = SAMPLE_FORMATS[data_format['sample_format']]
+    bits = sample_format['size_bytes'] * 8
+
+    command = sox_utils.generate_sine_tone_cmd(filename=sox_file_path,
+                                               channels=data_format['channel'],
+                                               bits=bits,
+                                               rate=data_format['rate'],
+                                               duration=duration_secs,
+                                               frequencies=frequencies,
+                                               vol=volume_scale,
+                                               raw=(data_format['file_type'] == 'raw'))
+
+    logging.info(' '.join(command))
+    subprocess.check_call(command)
+
+    test_data = audio_test_data.AudioTestData(data_format=data_format,
+                                              path=sox_file_path,
+                                              frequencies=frequencies,
+                                              duration_secs=duration_secs)
+
+    return test_data
+
+
+def generate_playback_file(audio_data):
+    """Generates the playback file if it does not exist yet.
+
+    Some audio test files may be large. Generate them on the fly to save the storage of the source tree.
+
+    Args:
+        audio_data: The audio test data.
+    """
+    directory = os.path.dirname(audio_data['file'])
+    if not os.path.exists(directory):
+        os.makedirs(directory)
+
+    if not os.path.exists(audio_data['file']):
+        data_format = dict(file_type='wav',
+                           sample_format='S16_LE',
+                           channel=audio_data['channels'],
+                           rate=audio_data['rate'])
+        generate_audio_test_data(data_format=data_format,
+                                 path=audio_data['file'],
+                                 duration_secs=audio_data['duration'],
+                                 frequencies=audio_data['frequencies'])
+        logging.debug('Audio file generated: %s', audio_data['file'])
+
+
+def generate_playback_file_from_binary_data(audio_data):
+    """Generates wav audio file from binary audio data.
+
+    Args:
+        audio_data: The binary audio data.
+    """
+    directory = os.path.dirname(A2DP_PLAYBACK_DATA['file'])
+    if not os.path.exists(directory):
+        os.makedirs(directory)
+
+    with wave.open(A2DP_PLAYBACK_DATA['file'], 'wb') as wav_file:
+        wav_file.setnchannels(A2DP_PLAYBACK_DATA['channels'])
+        wav_file.setframerate(A2DP_PLAYBACK_DATA['rate'])
+        wav_file.setsampwidth(A2DP_PLAYBACK_DATA['sample_width'])
+        wav_file.writeframes(audio_data)
+
+    logging.debug('wav file generated from binary data: %s', A2DP_PLAYBACK_DATA['file'])
diff --git a/floss/pandora/floss/bluetooth_gatt_service.py b/floss/pandora/floss/bluetooth_gatt_service.py
new file mode 100644
index 0000000..13ca763
--- /dev/null
+++ b/floss/pandora/floss/bluetooth_gatt_service.py
@@ -0,0 +1,181 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#     https://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.
+"""Class to hold the GATT service/characteristic/descriptor object."""
+
+import uuid
+
+
+class Service:
+    """Class represents Bluetooth GATT service."""
+
+    def __init__(self,
+                 instance_id=None,
+                 service_type=None,
+                 uuid=None,
+                 characteristics=None,
+                 included_services=None,
+                 value=None):
+        self.instance_id = instance_id
+        self.service_type = service_type
+        self.uuid = uuid
+        self.characteristics = characteristics
+        self.included_services = included_services
+        self.value = value
+
+    def to_dict(self):
+        """Converts service object to dictionary.
+
+        Returns:
+            GATT service as dict.
+        """
+        return {
+            'instance_id': self.instance_id,
+            'service_type': self.service_type,
+            'uuid': self.uuid,
+            'included_services': [service.to_dict() for service in self.included_services],
+            'characteristics': [characteristic.to_dict() for characteristic in self.characteristics],
+            'value': self.value
+        }
+
+
+class Characteristic:
+    """Class represents Bluetooth GATT characteristic."""
+
+    def __init__(self,
+                 instance_id=None,
+                 permissions=None,
+                 write_type=None,
+                 descriptors=None,
+                 uuid=None,
+                 key_size=None,
+                 properties=None,
+                 value=None):
+        self.instance_id = instance_id
+        self.permissions = permissions
+        self.write_type = write_type
+        self.descriptors = descriptors
+        self.uuid = uuid
+        self.key_size = key_size
+        self.properties = properties
+        self.value = value
+
+    def to_dict(self):
+        """Converts characteristic object to dictionary.
+
+        Returns:
+            GATT characteristic as dict.
+        """
+        return {
+            'properties': self.properties,
+            'permissions': self.permissions,
+            'uuid': self.uuid,
+            'instance_id': self.instance_id,
+            'descriptors': [descriptor.to_dict() for descriptor in self.descriptors],
+            'key_size': self.key_size,
+            'write_type': self.write_type,
+            'value': self.value
+        }
+
+
+class Descriptor:
+    """Class represents Bluetooth GATT descriptor."""
+
+    def __init__(self, permissions=None, uuid=None, instance_id=None, value=None):
+        self.permissions = permissions
+        self.uuid = uuid
+        self.instance_id = instance_id
+        self.value = value
+
+    def to_dict(self):
+        """Converts descriptor object to dictionary.
+
+        Returns:
+            GATT descriptor as dict.
+        """
+        return {
+            'instance_id': self.instance_id,
+            'permissions': self.permissions,
+            'uuid': self.uuid,
+            'value': self.value
+        }
+
+
+def create_gatt_service(service):
+    """Creates GATT service from a dictionary.
+
+    Args:
+        service: Bluetooth GATT service as a dictionary.
+
+    Returns:
+        Bluetooth GATT service object.
+    """
+    return Service(
+        instance_id=service['instance_id'],
+        service_type=service['service_type'],
+        uuid=str(uuid.UUID(bytes=bytes(service['uuid']))).upper(),
+        included_services=[create_gatt_service(included_service) for included_service in service['included_services']],
+        characteristics=[create_gatt_characteristic(characteristic) for characteristic in service['characteristics']],
+        value=service.get('value'))
+
+
+def create_gatt_characteristic(characteristic):
+    """Creates GATT characteristic from a dictionary.
+
+    Args:
+        characteristic: Bluetooth GATT characteristic as a dictionary.
+
+    Returns:
+        Bluetooth GATT characteristic object.
+    """
+    return Characteristic(
+        properties=characteristic['properties'],
+        permissions=characteristic['permissions'],
+        uuid=str(uuid.UUID(bytes=bytes(characteristic['uuid']))).upper(),
+        instance_id=characteristic['instance_id'],
+        descriptors=[create_gatt_characteristic_descriptor(descriptor) for descriptor in characteristic['descriptors']],
+        key_size=characteristic['key_size'],
+        write_type=characteristic['write_type'],
+        value=characteristic.get('value'))
+
+
+def create_gatt_characteristic_descriptor(descriptor):
+    """Creates GATT descriptor from a dictionary.
+
+    Args:
+        descriptor: Bluetooth GATT descriptor as a dictionary.
+
+    Returns:
+        Bluetooth GATT descriptor object.
+    """
+    return Descriptor(instance_id=descriptor['instance_id'],
+                      permissions=descriptor['permissions'],
+                      uuid=str(uuid.UUID(bytes=bytes(descriptor['uuid']))).upper(),
+                      value=descriptor.get('value'))
+
+
+def convert_object_to_dict(obj):
+    """Coverts object to dictionary.
+
+    Args:
+        obj: Service/Characteristic/Descriptor object.
+
+    Returns:
+        A dictionary represents the object.
+    """
+    if isinstance(obj, (Descriptor, Characteristic, Service)):
+        return obj.to_dict()
+    elif isinstance(obj, list):
+        return [convert_object_to_dict(item) for item in obj]
+    else:
+        return obj
diff --git a/floss/pandora/floss/cmd_utils.py b/floss/pandora/floss/cmd_utils.py
new file mode 100644
index 0000000..e8f10d6
--- /dev/null
+++ b/floss/pandora/floss/cmd_utils.py
@@ -0,0 +1,228 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#     https://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.
+
+import atexit
+import itertools
+import logging
+import os
+import pipes
+import pwd
+import select
+import subprocess
+import threading
+
+TEE_TO_LOGS = object()
+_popen_lock = threading.Lock()
+_logging_service = None
+_command_serial_number = itertools.count(1)
+
+_LOG_BUFSIZE = 4096
+_PIPE_CLOSED = -1
+
+
+class _LoggerProxy(object):
+
+    def __init__(self, logger):
+        self._logger = logger
+
+    def fileno(self):
+        """Returns the fileno of the logger pipe."""
+        return self._logger._pipe[1]
+
+    def __del__(self):
+        self._logger.close()
+
+
+class _PipeLogger(object):
+
+    def __init__(self, level, prefix):
+        self._pipe = list(os.pipe())
+        self._level = level
+        self._prefix = prefix
+
+    def close(self):
+        """Closes the logger."""
+        if self._pipe[1] != _PIPE_CLOSED:
+            os.close(self._pipe[1])
+            self._pipe[1] = _PIPE_CLOSED
+
+
+class _LoggingService(object):
+
+    def __init__(self):
+        # Python's list is thread safe
+        self._loggers = []
+
+        # Change tuple to list so that we can change the value when
+        # closing the pipe.
+        self._pipe = list(os.pipe())
+        self._thread = threading.Thread(target=self._service_run)
+        self._thread.daemon = True
+        self._thread.start()
+
+    def _service_run(self):
+        terminate_loop = False
+        while not terminate_loop:
+            rlist = [l._pipe[0] for l in self._loggers]
+            rlist.append(self._pipe[0])
+            for r in select.select(rlist, [], [])[0]:
+                data = os.read(r, _LOG_BUFSIZE)
+                if r != self._pipe[0]:
+                    self._output_logger_message(r, data)
+                elif len(data) == 0:
+                    terminate_loop = True
+        # Release resources.
+        os.close(self._pipe[0])
+        for logger in self._loggers:
+            os.close(logger._pipe[0])
+
+    def _output_logger_message(self, r, data):
+        logger = next(l for l in self._loggers if l._pipe[0] == r)
+
+        if len(data) == 0:
+            os.close(logger._pipe[0])
+            self._loggers.remove(logger)
+            return
+
+        for line in data.split('\n'):
+            logging.log(logger._level, '%s%s', logger._prefix, line)
+
+    def create_logger(self, level=logging.DEBUG, prefix=''):
+        """Creates a new logger.
+
+        Args:
+            level: The desired logging level.
+            prefix: The prefix to add to each log entry.
+        """
+        logger = _PipeLogger(level=level, prefix=prefix)
+        self._loggers.append(logger)
+        os.write(self._pipe[1], '\0')
+        return _LoggerProxy(logger)
+
+    def shutdown(self):
+        """Shuts down the logger."""
+        if self._pipe[1] != _PIPE_CLOSED:
+            os.close(self._pipe[1])
+            self._pipe[1] = _PIPE_CLOSED
+            self._thread.join()
+
+
+def create_logger(level=logging.DEBUG, prefix=''):
+    """Creates a new logger.
+
+    Args:
+        level: The desired logging level.
+        prefix: The prefix to add to each log entry.
+    """
+    global _logging_service
+    if _logging_service is None:
+        _logging_service = _LoggingService()
+        atexit.register(_logging_service.shutdown)
+    return _logging_service.create_logger(level=level, prefix=prefix)
+
+
+def wait_and_check_returncode(*popens):
+    """Waits for all the Popens and check the return code is 0.
+
+    Args:
+        popens: The Popens to be checked.
+
+    Raises:
+        RuntimeError if the return code is not 0.
+    """
+    error_message = None
+    for p in popens:
+        if p.wait() != 0:
+            error_message = ('Command failed(%d, %d): %s' % (p.pid, p.returncode, p.command))
+            logging.error(error_message)
+    if error_message:
+        raise RuntimeError(error_message)
+
+
+def execute(args, stdin=None, stdout=TEE_TO_LOGS, stderr=TEE_TO_LOGS, run_as=None):
+    """Executes a child command and wait for it.
+
+    Args:
+        args: The command to be executed.
+        stdin: The executed program's standard input.
+        stdout: The executed program's standard output.
+        stderr: The executed program's standard error.
+        run_as: If not None, run the command as the given user.
+
+    Returns:
+        The output from standard output if 'stdout' is subprocess.PIPE.
+
+    Raises:
+        RuntimeException if the return code of the child command is not 0.
+    """
+    ps = popen(args, stdin=stdin, stdout=stdout, stderr=stderr, run_as=run_as)
+    out = ps.communicate()[0] if stdout == subprocess.PIPE else None
+    wait_and_check_returncode(ps)
+    return out
+
+
+def _run_as(user):
+    """Changes the uid and gid of the running process to be that of the
+    given user and configures its supplementary groups.
+
+    Don't call this function directly, instead wrap it in a lambda and
+    pass that to the preexec_fn argument of subprocess.Popen.
+
+    Example usage:
+    subprocess.Popen(..., preexec_fn=lambda: _run_as('chronos'))
+
+    Args:
+        user: The user to run as.
+    """
+    pw = pwd.getpwnam(user)
+    os.setgid(pw.pw_gid)
+    os.initgroups(user, pw.pw_gid)
+    os.setuid(pw.pw_uid)
+
+
+def popen(args, stdin=None, stdout=TEE_TO_LOGS, stderr=TEE_TO_LOGS, env=None, run_as=None):
+    """Returns a Popen object just as subprocess.Popen does but with the
+    executed command stored in Popen.command.
+
+    Args:
+        args: The command to be executed.
+        stdin: The executed program's standard input.
+        stdout: The executed program's standard output.
+        stderr: The executed program's standard error.
+        env: The executed program's environment.
+        run_as: If not None, run the command as the given user.
+    """
+    command_id = next(_command_serial_number)
+    prefix = '[%04d] ' % command_id
+
+    if stdout is TEE_TO_LOGS:
+        stdout = create_logger(level=logging.DEBUG, prefix=prefix)
+    if stderr is TEE_TO_LOGS:
+        stderr = create_logger(level=logging.ERROR, prefix=prefix)
+
+    command = ' '.join(pipes.quote(x) for x in args)
+    logging.info('%sRunning: %s', prefix, command)
+
+    preexec_fn = None
+    if run_as is not None:
+        preexec_fn = lambda: _run_as(run_as)
+
+    # The lock is required for http://crbug.com/323843.
+    with _popen_lock:
+        ps = subprocess.Popen(args, stdin=stdin, stdout=stdout, stderr=stderr, env=env, preexec_fn=preexec_fn)
+
+    logging.info('%spid is %d', prefix, ps.pid)
+    ps.command_id = command_id
+    ps.command = command
+    return ps
diff --git a/floss/pandora/floss/cras_utils.py b/floss/pandora/floss/cras_utils.py
new file mode 100644
index 0000000..78241d0
--- /dev/null
+++ b/floss/pandora/floss/cras_utils.py
@@ -0,0 +1,852 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#     https://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.
+"""This module provides cras audio utilities."""
+
+import dbus
+import logging
+import re
+import subprocess
+
+from floss.pandora.floss import cmd_utils
+from floss.pandora.floss import utils
+
+_CRAS_TEST_CLIENT = '/usr/bin/cras_test_client'
+
+
+class CrasUtilsError(Exception):
+    """Error in CrasUtils."""
+    pass
+
+
+def playback(blocking=True, stdin=None, *args, **kargs):
+    """A helper function to execute the playback_cmd.
+
+    Args:
+        blocking: Blocks this call until playback finishes.
+        stdin: The standard input of playback process.
+        args: Args passed to playback_cmd.
+        kargs: Kargs passed to playback_cmd.
+
+    Returns:
+        The process running the playback command. Note that if the
+        blocking parameter is true, this will return a finished process.
+    """
+    process = cmd_utils.popen(playback_cmd(*args, **kargs), stdin=stdin)
+    if blocking:
+        cmd_utils.wait_and_check_returncode(process)
+    return process
+
+
+def capture(*args, **kargs):
+    """A helper function to execute the capture_cmd.
+
+    Args:
+        args: Args passed to capture_cmd.
+        kargs: Kargs passed to capture_cmd.
+    """
+    cmd_utils.execute(capture_cmd(*args, **kargs))
+
+
+def playback_cmd(playback_file, block_size=None, duration=None, pin_device=None, channels=2, rate=48000):
+    """Gets a command to playback a file with given settings.
+
+    Args:
+        playback_file: The name of the file to play. '-' indicates to playback raw audio from the stdin.
+        pin_device: The device id to playback on.
+        block_size: The number of frames per callback(dictates latency).
+        duration: Seconds to playback.
+        channels: Number of channels.
+        rate: The sampling rate.
+
+    Returns:
+        The command args put in a list of strings.
+    """
+    args = [_CRAS_TEST_CLIENT]
+    args += ['--playback_file', playback_file]
+    if pin_device is not None:
+        args += ['--pin_device', str(pin_device)]
+    if block_size is not None:
+        args += ['--block_size', str(block_size)]
+    if duration is not None:
+        args += ['--duration', str(duration)]
+    args += ['--num_channels', str(channels)]
+    args += ['--rate', str(rate)]
+    return args
+
+
+def capture_cmd(capture_file,
+                block_size=None,
+                duration=10,
+                sample_format='S16_LE',
+                pin_device=None,
+                channels=1,
+                rate=48000):
+    """Gets a command to capture the audio into the file with given settings.
+
+    Args:
+        capture_file: The name of file the audio to be stored in.
+        block_size: The number of frames per callback(dictates latency).
+        duration: Seconds to record. If it is None, duration is not set,
+                  and command will keep capturing audio until it is terminated.
+        sample_format: The sample format; possible choices: 'S16_LE', 'S24_LE',
+                       and 'S32_LE' default to S16_LE: signed 16 bits/sample, little endian.
+        pin_device: The device id to record from.
+        channels: Number of channels.
+        rate: The sampling rate.
+
+    Returns:
+        The command args put in a list of strings.
+    """
+    args = [_CRAS_TEST_CLIENT]
+    args += ['--capture_file', capture_file]
+    if pin_device is not None:
+        args += ['--pin_device', str(pin_device)]
+    if block_size is not None:
+        args += ['--block_size', str(block_size)]
+    if duration is not None:
+        args += ['--duration', str(duration)]
+    args += ['--num_channels', str(channels)]
+    args += ['--rate', str(rate)]
+    args += ['--format', str(sample_format)]
+    return args
+
+
+def listen_cmd(capture_file, block_size=None, duration=10, channels=1, rate=48000):
+    """Gets a command to listen on hotword and record audio into the file with given settings.
+
+    Args:
+        capture_file: The name of file the audio to be stored in.
+        block_size: The number of frames per callback(dictates latency).
+        duration: Seconds to record. If it is None, duration is not set, and command
+                  will keep capturing audio until it is terminated.
+        channels: Number of channels.
+        rate: The sampling rate.
+
+    Returns:
+        The command args put in a list of strings.
+    """
+    args = [_CRAS_TEST_CLIENT]
+    args += ['--listen_for_hotword', capture_file]
+    if block_size is not None:
+        args += ['--block_size', str(block_size)]
+    if duration is not None:
+        args += ['--duration', str(duration)]
+    args += ['--num_channels', str(channels)]
+    args += ['--rate', str(rate)]
+    return args
+
+
+def loopback(*args, **kargs):
+    """A helper function to execute loopback_cmd.
+
+    Args:
+        args: Args passed to loopback_cmd.
+        kargs: Kargs passed to loopback_cmd.
+    """
+    cmd_utils.execute(loopback_cmd(*args, **kargs))
+
+
+def loopback_cmd(output_file, duration=10, channels=2, rate=48000):
+    """Gets a command to record the loopback.
+
+    Args:
+        output_file: The name of the file the loopback to be stored in.
+        channels: The number of channels of the recorded audio.
+        duration: Seconds to record.
+        rate: The sampling rate.
+
+    Returns:
+        The command args put in a list of strings.
+    """
+    args = [_CRAS_TEST_CLIENT]
+    args += ['--loopback_file', output_file]
+    args += ['--duration_seconds', str(duration)]
+    args += ['--num_channels', str(channels)]
+    args += ['--rate', str(rate)]
+    return args
+
+
+def get_cras_nodes_cmd():
+    """Gets a command to query the nodes from Cras.
+
+    Returns:
+        The command to query nodes information from Cras using dbus-send.
+    """
+    return ('dbus-send --system --type=method_call --print-reply '
+            '--dest=org.chromium.cras /org/chromium/cras '
+            'org.chromium.cras.Control.GetNodes')
+
+
+def _dbus_uint64(x):
+    """Returns a UINT64 python-dbus object.
+
+    *Sometimes* python-dbus fails into the following cases:
+      - Attempt to convert a 64-bit integer into int32 and overflow
+      - Convert `dbus.UInt64(12345678900, variant_level=1)` (we usually get
+        this from some DBus calls) into VARIANT rather than UINT64
+
+    This function is a helper to avoid the above flakiness.
+    """
+    return dbus.types.UInt64(int(x), variant_level=0)
+
+
+def set_system_volume(volume):
+    """Sets the system volume.
+
+    Args:
+        volume: The system output vlume to be set(0 - 100).
+    """
+    get_cras_control_interface().SetOutputVolume(volume)
+
+
+def set_node_volume(node_id, volume):
+    """Sets the volume of the given output node.
+
+    Args:
+        node_id: The id of the output node to be set the volume.
+        volume: The volume to be set(0-100).
+    """
+    get_cras_control_interface().SetOutputNodeVolume(_dbus_uint64(node_id), volume)
+
+
+def get_cras_control_interface(private=False):
+    """Gets Cras DBus control interface.
+
+    Args:
+        private: Set to True to use a new instance for dbus.SystemBus instead of the shared instance.
+
+    Returns:
+        A dBus.Interface object with Cras Control interface.
+    """
+    bus = dbus.SystemBus(private=private)
+    cras_object = bus.get_object('org.chromium.cras', '/org/chromium/cras')
+    return dbus.Interface(cras_object, 'org.chromium.cras.Control')
+
+
+def get_cras_nodes():
+    """Gets nodes information from Cras.
+
+    Returns:
+        A dict containing information of each node.
+    """
+    return get_cras_control_interface().GetNodes()
+
+
+def get_selected_nodes():
+    """Gets selected output nodes and input nodes.
+
+    Returns:
+        A tuple (output_nodes, input_nodes) where each field is a list of selected
+        node IDs returned from Cras DBus API. Note that there may be multiple output/input
+        nodes being selected at the same time.
+    """
+    output_nodes = []
+    input_nodes = []
+    nodes = get_cras_nodes()
+    for node in nodes:
+        if node['Active']:
+            if node['IsInput']:
+                input_nodes.append(node['Id'])
+            else:
+                output_nodes.append(node['Id'])
+    return (output_nodes, input_nodes)
+
+
+def set_selected_output_node_volume(volume):
+    """Sets the selected output node volume.
+
+    Args:
+        volume: The volume to be set (0-100).
+    """
+    selected_output_node_ids, _ = get_selected_nodes()
+    for node_id in selected_output_node_ids:
+        set_node_volume(node_id, volume)
+
+
+def get_active_stream_count():
+    """Gets the number of active streams.
+
+    Returns:
+        The number of active streams.
+    """
+    return int(get_cras_control_interface().GetNumberOfActiveStreams())
+
+
+def set_system_mute(is_mute):
+    """Sets the system mute switch.
+
+    Args:
+        is_mute: Set True to mute the system playback.
+    """
+    get_cras_control_interface().SetOutputMute(is_mute)
+
+
+def set_capture_mute(is_mute):
+    """Sets the capture mute switch.
+
+    Args:
+        is_mute: Set True to mute the capture.
+    """
+    get_cras_control_interface().SetInputMute(is_mute)
+
+
+def node_type_is_plugged(node_type, nodes_info):
+    """Determines if there is any node of node_type plugged.
+
+    This method is used in the AudioLoopbackDongleLabel class, where the call is
+    executed on autotest server. Use get_cras_nodes instead if the call can be executed
+    on Cros device.
+
+    Since Cras only reports the plugged node in GetNodes, we can parse the return value
+    to see if there is any node with the given type. For example, if INTERNAL_MIC is of
+    intereset, the pattern we are looking for is:
+
+    dict entry(
+       string "Type"
+       variant             string "INTERNAL_MIC"
+    )
+
+    Args:
+        node_type: A str representing node type defined in CRAS_NODE_TYPES.
+        nodes_info: A str containing output of command get_nodes_cmd.
+
+    Returns:
+        True if there is any node of node_type plugged. False otherwise.
+    """
+    match = re.search(r'string "Type"\s+variant\s+string "%s"' % node_type, nodes_info)
+    return True if match else False
+
+
+# Cras node types reported from Cras DBus control API.
+CRAS_OUTPUT_NODE_TYPES = [
+    'HEADPHONE', 'INTERNAL_SPEAKER', 'HDMI', 'USB', 'BLUETOOTH', 'LINEOUT', 'UNKNOWN', 'ALSA_LOOPBACK'
+]
+CRAS_INPUT_NODE_TYPES = [
+    'MIC', 'INTERNAL_MIC', 'USB', 'BLUETOOTH', 'POST_DSP_LOOPBACK', 'POST_MIX_LOOPBACK', 'UNKNOWN', 'KEYBOARD_MIC',
+    'HOTWORD', 'FRONT_MIC', 'REAR_MIC', 'ECHO_REFERENCE'
+]
+CRAS_NODE_TYPES = CRAS_OUTPUT_NODE_TYPES + CRAS_INPUT_NODE_TYPES
+
+
+def get_filtered_node_types(callback):
+    """Returns the pair of filtered output node types and input node types.
+
+    Args:
+        callback: A callback function which takes a node as input parameter
+                  and filter the node based on its return value.
+
+    Returns:
+        A tuple (output_node_types, input_node_types) where each field is
+        a list of node types defined in CRAS_NODE_TYPES, and their 'attribute_name' is True.
+    """
+    output_node_types = []
+    input_node_types = []
+    nodes = get_cras_nodes()
+    for node in nodes:
+        if callback(node):
+            node_type = str(node['Type'])
+            if node_type not in CRAS_NODE_TYPES:
+                logging.warning('node type %s is not in known CRAS_NODE_TYPES', node_type)
+            if node['IsInput']:
+                input_node_types.append(node_type)
+            else:
+                output_node_types.append(node_type)
+    return (output_node_types, input_node_types)
+
+
+def get_selected_node_types():
+    """Returns the pair of active output node types and input node types.
+
+    Returns:
+         A tuple (output_node_types, input_node_types) where each field is a list
+         of selected node types defined in CRAS_NODE_TYPES.
+    """
+
+    def is_selected(node):
+        """Checks if a node is selected.
+
+        A node is selected if its Active attribute is True.
+
+        Returns:
+            True is a node is selected, False otherwise.
+        """
+        return node['Active']
+
+    return get_filtered_node_types(is_selected)
+
+
+def get_selected_output_device_name():
+    """Returns the device name of the active output node.
+
+    Returns:
+        device name string. E.g. mtk-rt5650: :0,0.
+    """
+    nodes = get_cras_nodes()
+    for node in nodes:
+        if node['Active'] and not node['IsInput']:
+            return node['DeviceName']
+    return None
+
+
+def get_selected_output_device_type():
+    """Returns the device type of the active output node.
+
+    Returns:
+        device type string. E.g. INTERNAL_SPEAKER.
+    """
+    nodes = get_cras_nodes()
+    for node in nodes:
+        if node['Active'] and not node['IsInput']:
+            return node['Type']
+    return None
+
+
+def set_single_selected_output_node(node_type):
+    """Sets one selected output node.
+
+    Note that Chrome UI uses SetActiveOutputNode of Cras DBus API to select one output node.
+
+    Args:
+        node_type: A node type.
+
+    Returns:
+        True if the output node type is found and set active.
+    """
+    nodes = get_cras_nodes()
+    for node in nodes:
+        if node['IsInput']:
+            continue
+        if node['Type'] == node_type:
+            set_active_output_node(node['Id'])
+            return True
+    return False
+
+
+def set_selected_output_nodes(types):
+    """Sets selected output node types.
+
+    Note that Chrome UI uses SetActiveOutputNode of Cras DBus API to select one
+    output node. Here we use add/remove active output node to support multiple nodes.
+
+    Args:
+        types: A list of output node types.
+    """
+    nodes = get_cras_nodes()
+    for node in nodes:
+        if node['IsInput']:
+            continue
+        if node['Type'] in types:
+            add_active_output_node(node['Id'])
+        elif node['Active']:
+            remove_active_output_node(node['Id'])
+
+
+def set_active_output_node(node_id):
+    """Sets one active output node.
+
+    Args:
+        node_id: node id.
+    """
+    get_cras_control_interface().SetActiveOutputNode(_dbus_uint64(node_id))
+
+
+def add_active_output_node(node_id):
+    """Adds an active output node.
+
+    Args:
+        node_id: node id.
+    """
+    get_cras_control_interface().AddActiveOutputNode(_dbus_uint64(node_id))
+
+
+def remove_active_output_node(node_id):
+    """Removes an active output node.
+
+    Args:
+        node_id: node id.
+    """
+    get_cras_control_interface().RemoveActiveOutputNode(_dbus_uint64(node_id))
+
+
+def get_node_id_from_node_type(node_type, is_input):
+    """Gets node id from node type.
+
+    Args:
+        node_type: A node type defined in CRAS_NODE_TYPES.
+        is_input: True if the node is input. False otherwise.
+
+    Returns:
+        A string for node id.
+
+    Raises:
+        CrasUtilsError: if unique node id can not be found.
+    """
+    nodes = get_cras_nodes()
+    find_ids = []
+    for node in nodes:
+        if node['Type'] == node_type and node['IsInput'] == is_input:
+            find_ids.append(node['Id'])
+    if len(find_ids) != 1:
+        raise CrasUtilsError('Can not find unique node id from node type %s' % node_type)
+    return find_ids[0]
+
+
+def get_device_id_of(node_id):
+    """Gets the device id of the node id.
+
+    The conversion logic is replicated from the CRAS's type definition at
+    third_party/adhd/cras/src/common/cras_types.h.
+
+    Args:
+        node_id: A string for node id.
+
+    Returns:
+        A string for device id.
+
+    Raises:
+        CrasUtilsError: if device id is invalid.
+    """
+    device_id = str(int(node_id) >> 32)
+    if device_id == "0":
+        raise CrasUtilsError('Got invalid device_id: 0')
+    return device_id
+
+
+def get_device_id_from_node_type(node_type, is_input):
+    """Gets device id from node type.
+
+    Args:
+        node_type: A node type defined in CRAS_NODE_TYPES.
+        is_input: True if the node is input. False otherwise.
+
+    Returns:
+        A string for device id.
+    """
+    node_id = get_node_id_from_node_type(node_type, is_input)
+    return get_device_id_of(node_id)
+
+
+def set_floss_enabled(enabled):
+    """Sets whether CRAS stack expects to use Floss.
+
+    Args:
+        enabled: True for Floss, False for Bluez.
+    """
+    get_cras_control_interface().SetFlossEnabled(enabled)
+
+
+class CrasTestClient(object):
+    """An object to perform cras_test_client functions."""
+
+    BLOCK_SIZE = None
+    PIN_DEVICE = None
+    SAMPLE_FORMAT = 'S16_LE'
+    DURATION = 10
+    CHANNELS = 2
+    RATE = 48000
+
+    def __init__(self):
+        self._proc = None
+        self._capturing_proc = None
+        self._playing_proc = None
+        self._capturing_msg = 'capturing audio file'
+        self._playing_msg = 'playing audio file'
+        self._wbs_cmd = '%s --set_wbs_enabled ' % _CRAS_TEST_CLIENT
+        self._enable_wbs_cmd = ('%s 1' % self._wbs_cmd).split()
+        self._disable_wbs_cmd = ('%s 0' % self._wbs_cmd).split()
+        self._info_cmd = [
+            _CRAS_TEST_CLIENT,
+        ]
+        self._select_input_cmd = '%s --select_input ' % _CRAS_TEST_CLIENT
+
+    def start_subprocess(self, proc, proc_cmd, filename, proc_msg):
+        """Starts a capture or play subprocess
+
+        Args:
+            proc: The process.
+            proc_cmd: The process command and its arguments.
+            filename: The file name to capture or play.
+            proc_msg: The message to display in logging.
+
+        Returns:
+            True if the process is started successfully
+        """
+        if proc is None:
+            try:
+                self._proc = subprocess.Popen(proc_cmd)
+                logging.debug('Start %s %s on the DUT', proc_msg, filename)
+            except Exception as e:
+                logging.error('Failed to popen: %s (%s)', proc_msg, e)
+                return False
+        else:
+            logging.error('cannot run the command twice: %s', proc_msg)
+            return False
+        return True
+
+    def stop_subprocess(self, proc, proc_msg):
+        """Stops a subprocess
+
+        Args:
+            proc: The process to stop.
+            proc_msg: The message to display in logging.
+
+        Returns:
+            True if the process is stopped successfully.
+        """
+        if proc is None:
+            logging.error('cannot run stop %s before starting it.', proc_msg)
+            return False
+
+        proc.terminate()
+        try:
+            utils.poll_for_condition(condition=lambda: proc.poll() is not None,
+                                     exception=CrasUtilsError,
+                                     timeout=10,
+                                     sleep_interval=0.5,
+                                     desc='Waiting for subprocess to terminate')
+        except Exception:
+            logging.warn('Killing subprocess due to timeout')
+            proc.kill()
+            proc.wait()
+
+        logging.debug('stop %s on the DUT', proc_msg)
+        return True
+
+    def start_capturing_subprocess(self,
+                                   capture_file,
+                                   block_size=BLOCK_SIZE,
+                                   duration=DURATION,
+                                   pin_device=PIN_DEVICE,
+                                   sample_format=SAMPLE_FORMAT,
+                                   channels=CHANNELS,
+                                   rate=RATE):
+        """Starts capturing in a subprocess.
+
+        Args:
+            capture_file: The name of file the audio to be stored in.
+            block_size: The number of frames per callback(dictates latency).
+            duration: Seconds to record. If it is None, duration is not set, and
+                      will keep capturing audio until terminated.
+            sample_format: The sample format.
+            pin_device: The device id to record from.
+            channels: Number of channels.
+            rate: The sampling rate.
+
+        Returns:
+            True if the process is started successfully.
+        """
+        proc_cmd = capture_cmd(capture_file,
+                               block_size=block_size,
+                               duration=duration,
+                               sample_format=sample_format,
+                               pin_device=pin_device,
+                               channels=channels,
+                               rate=rate)
+        result = self.start_subprocess(self._capturing_proc, proc_cmd, capture_file, self._capturing_msg)
+        if result:
+            self._capturing_proc = self._proc
+        return result
+
+    def stop_capturing_subprocess(self):
+        """Stops the capturing subprocess."""
+        result = self.stop_subprocess(self._capturing_proc, self._capturing_msg)
+        if result:
+            self._capturing_proc = None
+        return result
+
+    def start_playing_subprocess(self,
+                                 audio_file,
+                                 block_size=BLOCK_SIZE,
+                                 duration=DURATION,
+                                 pin_device=PIN_DEVICE,
+                                 channels=CHANNELS,
+                                 rate=RATE):
+        """Starts playing the audio file in a subprocess.
+
+        Args:
+            audio_file: The name of audio file to play.
+            block_size: The number of frames per callback(dictates latency).
+            duration: Seconds to play. If it is None, duration is not set, and
+                      will keep playing audio until terminated.
+            pin_device: The device id to play to.
+            channels: Number of channels.
+            rate: The sampling rate.
+
+        Returns:
+            True if the process is started successfully.
+        """
+        proc_cmd = playback_cmd(audio_file, block_size, duration, pin_device, channels, rate)
+        result = self.start_subprocess(self._playing_proc, proc_cmd, audio_file, self._playing_msg)
+        if result:
+            self._playing_proc = self._proc
+        return result
+
+    def stop_playing_subprocess(self):
+        """Stops the playing subprocess."""
+        result = self.stop_subprocess(self._playing_proc, self._playing_msg)
+        if result:
+            self._playing_proc = None
+        return result
+
+    def play(self,
+             audio_file,
+             block_size=BLOCK_SIZE,
+             duration=DURATION,
+             pin_device=PIN_DEVICE,
+             channels=CHANNELS,
+             rate=RATE):
+        """Plays the audio file.
+
+        This method will get blocked until it has completed playing back. If you
+        do not want to get blocked, use start_playing_subprocess() above instead.
+
+        Args:
+            audio_file: The name of audio file to play.
+            block_size: The number of frames per callback(dictates latency).
+            duration: Seconds to play. If it is None, duration is not set, and
+                      will keep playing audio until terminated.
+            pin_device: The device id to play to.
+            channels: Number of channels.
+            rate: The sampling rate.
+
+        Returns:
+            True if the process is started successfully.
+        """
+        proc_cmd = playback_cmd(audio_file, block_size, duration, pin_device, channels, rate)
+        try:
+            self._proc = subprocess.call(proc_cmd)
+            logging.debug('call "%s" on the DUT', proc_cmd)
+        except Exception as e:
+            logging.error('Failed to call: %s (%s)', proc_cmd, e)
+            return False
+        return True
+
+    def enable_wbs(self, value):
+        """Enables or disable wideband speech (wbs) per the value.
+
+        Args:
+            value: True to enable wbs.
+
+        Returns:
+            True if the operation succeeds.
+        """
+        cmd = self._enable_wbs_cmd if value else self._disable_wbs_cmd
+        logging.debug('call "%s" on the DUT', cmd)
+        if subprocess.call(cmd):
+            logging.error('Failed to call: %s (%s)', cmd)
+            return False
+        return True
+
+    def select_input_device(self, device_name):
+        """Selects the audio input device.
+
+        Args:
+            device_name: The name of the Bluetooth peer device.
+
+        Returns:
+            True if the operation succeeds.
+        """
+        logging.debug('to select input device for device_name: %s', device_name)
+        try:
+            info = subprocess.check_output(self._info_cmd)
+            logging.debug('info: %s', info)
+        except Exception as e:
+            logging.error('Failed to call: %s (%s)', self._info_cmd, e)
+            return False
+
+        flag_input_nodes = False
+        audio_input_node = None
+        for line in info.decode().splitlines():
+            if 'Input Nodes' in line:
+                flag_input_nodes = True
+            elif 'Attached clients' in line:
+                flag_input_nodes = False
+
+            if flag_input_nodes:
+                if device_name in line:
+                    audio_input_node = line.split()[1]
+                    logging.debug('%s', audio_input_node)
+                    break
+
+        if audio_input_node is None:
+            logging.error('Failed to find audio input node: %s', device_name)
+            return False
+
+        select_input_cmd = (self._select_input_cmd + audio_input_node).split()
+        if subprocess.call(select_input_cmd):
+            logging.error('Failed to call: %s (%s)', select_input_cmd)
+            return False
+
+        logging.debug('call "%s" on the DUT', select_input_cmd)
+        return True
+
+    def set_player_playback_status(self, status):
+        """Sets playback status for the registered media player.
+
+        Args:
+            status: Playback status in string.
+        """
+        try:
+            get_cras_control_interface().SetPlayerPlaybackStatus(status)
+        except Exception as e:
+            logging.error('Failed to set player playback status: %s', e)
+            return False
+
+        return True
+
+    def set_player_position(self, position):
+        """Sets media position for the registered media player.
+
+        Args:
+            position: Position in micro seconds.
+        """
+        try:
+            get_cras_control_interface().SetPlayerPosition(position)
+        except Exception as e:
+            logging.error('Failed to set player position: %s', e)
+            return False
+
+        return True
+
+    def set_player_metadata(self, metadata):
+        """Sets title, artist, and album for the registered media player.
+
+        Args:
+            metadata: Dictionary of media metadata.
+        """
+        try:
+            get_cras_control_interface().SetPlayerMetadata(metadata)
+        except Exception as e:
+            logging.error('Failed to set player metadata: %s', e)
+            return False
+
+        return True
+
+    def set_player_length(self, length):
+        """Sets metadata length for the registered media player.
+
+        Media length is a part of metadata information. However, without specify
+        its type to int64. dbus-python will guess the variant type to be int32 by
+        default. Separate it from the metadata function to help prepare the data
+        differently.
+
+        Args:
+            length: DBUS dictionary that contains a variant of int64.
+        """
+        try:
+            get_cras_control_interface().SetPlayerMetadata(length)
+        except Exception as e:
+            logging.error('Failed to set player length: %s', e)
+            return False
+        return True
diff --git a/floss/pandora/floss/floss_enums.py b/floss/pandora/floss/floss_enums.py
index 01e8582..80e54a1 100644
--- a/floss/pandora/floss/floss_enums.py
+++ b/floss/pandora/floss/floss_enums.py
@@ -47,6 +47,31 @@
     PHY_CODED = 3
 
 
+class GattPermission(enum.IntEnum):
+    """Bluetooth GATT permissions."""
+    PERM_READ = (1 << 0)
+    PERM_READ_ENCRYPTED = (1 << 1)
+    PERM_READ_ENC_MITM = (1 << 2)
+    PERM_WRITE = (1 << 4)
+    PERM_WRITE_ENCRYPTED = (1 << 5)
+    PERM_WRITE_ENC_MITM = (1 << 6)
+    PERM_WRITE_SIGNED = (1 << 7)
+    PERM_WRITE_SIGNED_MITM = (1 << 8)
+    PERM_READ_IF_ENCRYPTED_OR_DISCOVERABLE = (1 << 9)
+
+
+class GattCharacteristicProprieties(enum.IntEnum):
+    """Bluetooth GATT characteristic proprieties."""
+    CHAR_PROP_BIT_BROADCAST = (1 << 0)
+    CHAR_PROP_BIT_READ = (1 << 1)
+    CHAR_PROP_BIT_WRITE_NR = (1 << 2)
+    CHAR_PROP_BIT_WRITE = (1 << 3)
+    CHAR_PROP_BIT_NOTIFY = (1 << 4)
+    CHAR_PROP_BIT_INDICATE = (1 << 5)
+    CHAR_PROP_BIT_AUTH = (1 << 6)
+    CHAR_PROP_BIT_EXT_PROP = (1 << 7)
+
+
 class GattStatus(enum.IntEnum):
     """Bluetooth GATT return status."""
     SUCCESS = 0x00
diff --git a/floss/pandora/floss/gatt_server.py b/floss/pandora/floss/gatt_server.py
index a3b21cb..f23e042 100644
--- a/floss/pandora/floss/gatt_server.py
+++ b/floss/pandora/floss/gatt_server.py
@@ -1,20 +1,22 @@
 # Copyright 2023 Google LLC
 #
-# Licensed under the Apache License, Version 2.0 (the "License");
+# 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
 #
 #     https://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,
+# 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.
 """Client Class to access the Floss GATT server interface."""
 
+import collections
 import logging
 
+from floss.pandora.floss import bluetooth_gatt_service
 from floss.pandora.floss import floss_enums
 from floss.pandora.floss import observer_base
 from floss.pandora.floss import utils
@@ -90,31 +92,31 @@
         """
         pass
 
-    def on_characteristic_write_request(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+    def on_characteristic_write_request(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
         """Called when there is a request to write a characteristic.
 
         Args:
             addr: Remote device MAC address.
             trans_id: Transaction id.
             offset: Represents the offset at which the attribute value should be written.
-            len: The length of the attribute value that should be written.
-            is_prep: A boolean value representing whether it's a "prepare write" command.
-            need_rsp: A boolean value representing whether it's a "write no response" command.
+            length: The length of the attribute value that should be written.
+            is_prep: A boolean value representing whether it's a 'prepare write' command.
+            need_rsp: A boolean value representing whether it's a 'write no response' command.
             handle: The characteristic handle.
             value: The value that should be written to the attribute.
         """
         pass
 
-    def on_descriptor_write_request(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+    def on_descriptor_write_request(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
         """Called when there is a request to write a descriptor.
 
         Args:
             addr: Remote device MAC address.
             trans_id: Transaction id.
             offset: The offset value at which the value should be written.
-            len: The length of the value that should be written.
-            is_prep: A boolean value representing whether it's a "prepare write" command.
-            need_rsp: A boolean value representing whether it's a "write no response" command.
+            length: The length of the value that should be written.
+            is_prep: A boolean value representing whether it's a 'prepare write' command.
+            need_rsp: A boolean value representing whether it's a 'write no response' command.
             handle: The descriptor handle.
             value: The value that should be written to the descriptor.
         """
@@ -196,6 +198,18 @@
         pass
 
 
+def uuid16_to_uuid128(uuid):
+    """Converts 16-bit UUID to 128-bit UUID.
+
+    Args:
+        uuid: 16-bit UUID value.
+
+    Returns:
+        128-bit UUID.
+    """
+    return utils.uuid16_to_uuid128(uuid).upper()
+
+
 class FlossGattServer(GattServerCallbacks):
     """Handles method calls and callbacks from the GATT server interface."""
 
@@ -205,6 +219,48 @@
     GATT_CB_OBJ_NAME = 'test_gatt_server'
     CB_EXPORTED_INTF = 'org.chromium.bluetooth.BluetoothGattServerCallback'
 
+    # Bluetooth GATT DataBase attribute type.
+    BTGATT_DB_PRIMARY_SERVICE = 0
+    BTGATT_DB_SECONDARY_SERVICE = 1
+    BTGATT_DB_INCLUDED_SERVICE = 2
+    BTGATT_DB_CHARACTERISTIC = 3
+    BTGATT_DB_DESCRIPTOR = 4
+
+    GATT_READ_AUTH_REQUIRED = floss_enums.GattPermission.PERM_READ_ENCRYPTED
+    GATT_READ_MITM_REQUIRED = floss_enums.GattPermission.PERM_READ_ENC_MITM
+    GATT_WRITE_AUTH_REQUIRED = (floss_enums.GattPermission.PERM_WRITE_ENCRYPTED |
+                                floss_enums.GattPermission.PERM_WRITE_SIGNED)
+    GATT_WRITE_MITM_REQUIRED = (floss_enums.GattPermission.PERM_WRITE_ENC_MITM |
+                                floss_enums.GattPermission.PERM_WRITE_SIGNED_MITM)
+
+    SERVICE_ATTR_UUID = uuid16_to_uuid128('7777')
+    SERVER_CHANGED_CHAR_UUID = uuid16_to_uuid128('2a05')
+    SERVER_SUP_FEAT_UUID = uuid16_to_uuid128('2b3a')
+    CLIENT_SUP_FEAT_UUID = uuid16_to_uuid128('2b29')
+    DATABASE_HASH_UUID = uuid16_to_uuid128('2b2a')
+    LONG_CHAR_UUID = uuid16_to_uuid128('b000')
+    DESCRIPTOR_UUID = uuid16_to_uuid128('b001')
+    KEY_SIZE_CHAR_UUID = uuid16_to_uuid128('b002')
+    UNAUTHORIZED_CHAR_UUID = uuid16_to_uuid128('b003')
+    AUTHENTICATION_CHAR_UUID = uuid16_to_uuid128('b004')
+    INVALID_FOR_LE_UUID = uuid16_to_uuid128('b005')
+    INVALID_FOR_BR_EDR_UUID = uuid16_to_uuid128('b006')
+    NO_READ_CHAR_UUID = uuid16_to_uuid128('b007')
+    NO_WRITE_CHAR_UUID = uuid16_to_uuid128('b008')
+    SHORT_CHAR_UUID = uuid16_to_uuid128('b009')
+    WRITE_NO_RESPONSE_CHAR_UUID = uuid16_to_uuid128('b00a')
+    NOTIFY_CHAR_UUID = uuid16_to_uuid128('b00b')
+    AUTHENTICATE_SHORT_CHAR_UUID = uuid16_to_uuid128('b00c')
+    CCC_DESCRIPTOR_UUID = uuid16_to_uuid128('2902')
+    LONG_CHAR_512_UUID = uuid16_to_uuid128('b00d')
+    MITM_SHORT_CHAR_UUID = uuid16_to_uuid128('b00e')
+
+    LONG_TEST_VALUE_512 = [bytes([7])] * 512
+    LONG_TEST_VALUE = [bytes([7])] * 48
+    SHORT_TEST_VALUE = [bytes([7])] * 2
+    NOTIFICATION_VALUE = [bytes([1]), bytes([0])]
+    INDICATION_VALUE = [bytes([2]), bytes([0])]
+
     class ExportedGattServerCallbacks(observer_base.ObserverBase):
         """
         <node>
@@ -244,7 +300,7 @@
                     <arg type="s" name="addr" direction="in" />
                     <arg type="i" name="trans_id" direction="in" />
                     <arg type="i" name="offset" direction="in" />
-                    <arg type="i" name="len" direction="in" />
+                    <arg type="i" name="length" direction="in" />
                     <arg type="b" name="is_prep" direction="in" />
                     <arg type="b" name="need_rsp" direction="in" />
                     <arg type="i" name="handle" direction="in" />
@@ -254,7 +310,7 @@
                     <arg type="s" name="addr" direction="in" />
                     <arg type="i" name="trans_id" direction="in" />
                     <arg type="i" name="offset" direction="in" />
-                    <arg type="i" name="len" direction="in" />
+                    <arg type="i" name="length" direction="in" />
                     <arg type="b" name="is_prep" direction="in" />
                     <arg type="b" name="need_rsp" direction="in" />
                     <arg type="i" name="handle" direction="in" />
@@ -363,6 +419,23 @@
             for observer in self.observers.values():
                 observer.on_characteristic_read_request(addr, trans_id, offset, is_long, handle)
 
+        def OnCharacteristicWriteRequest(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
+            """Handles characteristic write request callback.
+
+            Args:
+                addr: Remote device MAC address.
+                trans_id: Transaction id.
+                offset: Represents the offset from which the attribute value should be read.
+                length: The length of the value that should be written.
+                is_prep: A boolean value representing whether it's a 'prepare write' command.
+                need_rsp: A boolean value representing whether it's a 'write no response' command.
+                handle: The descriptor handle.
+                value: The value that should be written to the descriptor.
+            """
+            for observer in self.observers.values():
+                observer.on_characteristic_write_request(addr, trans_id, offset, length, is_prep, need_rsp, handle,
+                                                         value)
+
         def OnDescriptorReadRequest(self, addr, trans_id, offset, is_long, handle):
             """Handles descriptor read request callback.
 
@@ -377,37 +450,21 @@
             for observer in self.observers.values():
                 observer.on_descriptor_read_request(addr, trans_id, offset, is_long, handle)
 
-        def OnCharacteristicWrite(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
-            """Handles characteristic write request callback.
-
-            Args:
-                addr: Remote device MAC address.
-                trans_id: Transaction id.
-                offset: Represents the offset at which the attribute value should be written.
-                len: The length of the attribute value that should be written.
-                is_prep: A boolean value representing whether it's a "prepare write" command.
-                need_rsp: A boolean value representing whether it's a "write no response" command.
-                handle: The characteristic handle.
-                value: The value that should be written to the attribute.
-            """
-            for observer in self.observers.values():
-                observer.on_characteristic_write_request(addr, trans_id, offset, len, is_prep, need_rsp, handle, value)
-
-        def OnDescriptorWriteRequest(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+        def OnDescriptorWriteRequest(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
             """Handles descriptor write request callback.
 
             Args:
                 addr: Remote device MAC address.
                 trans_id: Transaction id.
                 offset: The offset value at which the value should be written.
-                len: The length of the value that should be written.
-                is_prep: A boolean value representing whether it's a "prepare write" command.
-                need_rsp: A boolean value representing whether it's a "write no response" command.
+                length: The length of the value that should be written.
+                is_prep: A boolean value representing whether it's a 'prepare write' command.
+                need_rsp: A boolean value representing whether it's a 'write no response' command.
                 handle: The descriptor handle.
                 value: The value that should be written to the descriptor.
             """
             for observer in self.observers.values():
-                observer.on_descriptor_write_request(addr, trans_id, offset, len, is_prep, need_rsp, handle, value)
+                observer.on_descriptor_write_request(addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
 
         def OnExecuteWrite(self, addr, trans_id, exec_write):
             """Handles execute write callback.
@@ -505,17 +562,527 @@
         self.objpath = self.GATT_OBJECT_PATTERN.format(hci)
         self.cb_dbus_objpath = utils.generate_dbus_cb_objpath(self.GATT_CB_OBJ_NAME, self.hci)
 
-        # Create and publish callbacks
+        # Create and publish callbacks.
         self.callbacks = self.ExportedGattServerCallbacks()
         self.callbacks.add_observer('gatt_testing_server', self)
         self.bus.register_object(self.cb_dbus_objpath, self.callbacks, None)
-        self.server_connect_id = None
         self.server_id = None
+        self.mtu_value = -1
+        self.write_requests = collections.deque([])
+        self.gatt_services = []
+        # Indicate if PTS attribute values were set or not (set only one time).
+        self.pts_set_values = False
 
     def __del__(self):
         """Destructor."""
         del self.callbacks
 
+    def check_permissions(self, uuid):
+        """Checks request UUID permission.
+
+        Args:
+            uuid: 128-bit UUID value as string.
+
+        Returns:
+            GATT status.
+        """
+        if uuid is None:
+            logging.debug('check_permissions uuid is None or value is None')
+            return floss_enums.GattStatus.NOT_FOUND
+        elif uuid == self.LONG_CHAR_UUID:
+            logging.debug('check_permissions: uuid == long_char, return GATT_SUCCESS')
+        elif uuid == self.DESCRIPTOR_UUID:
+            logging.debug('check_permissions: uuid == descriptor, return GATT_SUCCESS')
+        elif uuid == self.UNAUTHORIZED_CHAR_UUID:
+            logging.debug('check_permissions: uuid == unauthorize_char, return GATT_INSUF_AUTHORIZATION')
+            return floss_enums.GattStatus.INSUF_AUTHORIZATION
+        elif uuid == self.AUTHENTICATION_CHAR_UUID:
+            logging.debug('check_permissions: uuid == authenticate_char, return GATT_SUCCESS')
+        elif uuid == self.INVALID_FOR_LE_UUID:
+            logging.debug('check_permissions: uuid == invalid_for_le, return GATT_SUCCESS')
+            return floss_enums.GattStatus.INTERNAL_ERROR
+        elif uuid == self.INVALID_FOR_BR_EDR_UUID:
+            logging.debug('check_permissions: uuid == invalid_for_bredr, return GATT_SUCCESS')
+            return floss_enums.GattStatus.INTERNAL_ERROR
+        elif uuid == self.NO_READ_CHAR_UUID:
+            logging.debug('check_permissions: uuid == no_read_char, return GATT_READ_NOT_PERMIT')
+            return floss_enums.GattStatus.READ_NOT_PERMIT
+        elif uuid == self.NO_WRITE_CHAR_UUID:
+            logging.debug('check_permissions: uuid == no_write_char, return GATT_WRITE_NOT_PERMIT')
+            return floss_enums.GattStatus.WRITE_NOT_PERMIT
+        elif uuid == self.SHORT_CHAR_UUID:
+            logging.debug('check_permissions: uuid == short_char, return GATT_SUCCESS')
+        elif uuid == self.WRITE_NO_RESPONSE_CHAR_UUID:
+            logging.debug('check_permissions: uuid == write_no_rsp_char, return GATT_SUCCESS')
+        elif uuid == self.AUTHENTICATE_SHORT_CHAR_UUID:
+            logging.debug('check_permissions: uuid == authenticate_short_char, return GATT_SUCCESS')
+        elif uuid == self.CCC_DESCRIPTOR_UUID:
+            logging.debug('check_permissions: uuid == ccc_descriptor, return GATT_SUCCESS')
+        elif uuid == self.LONG_CHAR_512_UUID:
+            logging.debug('check_permissions: uuid == long_char512, return GATT_SUCCESS')
+        elif uuid == self.MITM_SHORT_CHAR_UUID:
+            logging.debug('check_permissions: uuid == mitm_short_char, return GATT_SUCCESS')
+        else:
+            logging.debug('check_permissions: uuid: %s unknown return GATT_NOT_FOUND', uuid)
+            return floss_enums.GattStatus.NOT_FOUND
+        return floss_enums.GattStatus.SUCCESS
+
+    def generic_write(self, offset, length, handle, value):
+        """Writes GATT attribute value.
+
+        Args:
+            offset: Represents the offset at which the attribute value should be written.
+            length: The length of the attribute value that should be written.
+            handle: The attribute handle.
+            value: The value that should be written to the attribute.
+
+        Returns:
+            (Bluetooth Gatt status, attribute value).
+        """
+        attribute = self.get_attribute_from_handle(handle)
+        attr_value = attribute.value if attribute is not None else []
+
+        if len(attr_value) < offset:
+            logging.info('len(char_value) < offset')
+            return floss_enums.GattStatus.INVALID_OFFSET, []
+
+        if offset + length > len(attr_value):
+            logging.info('offset + len > len(char_value)')
+            return floss_enums.GattStatus.INVALID_ATTRLEN, []
+
+        attr_value[offset:(offset + length)] = value
+        self.update_attribute_value(attribute.uuid, attr_value)
+        return floss_enums.GattStatus.SUCCESS, attr_value
+
+    def generic_read(self, offset, handle):
+        """Reads GATT attribute value.
+
+        Args:
+            offset: Represents the offset from which the attribute value should be read.
+            handle: The attribute handle.
+
+        Returns:
+            (Bluetooth Gatt status, attribute value).
+        """
+        attr_value = self.get_attribute_value_from_handle(handle)
+
+        if offset < 0 or offset > len(attr_value):
+            logging.info('generic_read len(value) < offset')
+            return floss_enums.GattStatus.INVALID_OFFSET, []
+
+        return floss_enums.GattStatus.SUCCESS, attr_value[offset:]
+
+    def get_uuid_from_handle(self, handle):
+        """Gets attribute UUID from handle.
+
+        Args:
+            handle: The attribute handle.
+
+        Returns:
+            Attribute UUID as string if found, empty string otherwise.
+        """
+        attribute = self.get_attribute_from_handle(handle)
+        return '' if attribute is None else attribute.uuid
+
+    def get_attribute_value_from_handle(self, handle):
+        """Gets attribute value from handle.
+
+        Args:
+            handle: The attribute handle.
+
+        Returns:
+            Attribute value as list if found, empty list otherwise.
+        """
+        attribute = self.get_attribute_from_handle(handle)
+        return [] if attribute is None else attribute.value
+
+    def get_attribute_from_handle(self, handle):
+        """Gets GATT attribute from handle.
+
+        Args:
+            handle: The attribute handle.
+
+        Returns:
+            GATT attribute if found, None otherwise.
+        """
+        for service in self.gatt_services:
+            if int(service.instance_id) == int(handle):
+                return service
+            for char in service.characteristics:
+                if int(char.instance_id) == int(handle):
+                    return char
+                for desc in char.descriptors:
+                    if int(desc.instance_id) == int(handle):
+                        return desc
+
+        return None
+
+    def update_attribute_value(self, uuid, value):
+        """Update GATT attribute value.
+
+        Args:
+            uuid: GATT attribute uuid as string.
+            value: Attribute value as list.
+        """
+        for service in self.gatt_services:
+            if service.uuid == uuid:
+                service.value = value
+                return
+            for char in service.characteristics:
+                if char.uuid == uuid:
+                    char.value = value
+                    return
+                for desc in char.descriptors:
+                    if desc.uuid == uuid:
+                        desc.value = value
+                        return
+        logging.error('No attribute found with uuid = %s!', uuid)
+
+    def on_attr_read(self, addr, trans_id, offset, is_long, handle):
+        """Handles the read request for GATT attribute.
+
+        Args:
+            addr: Remote device MAC address.
+            trans_id: Transaction id.
+            offset: Represents the offset from which the attribute value should be read.
+            is_long: A boolean value representing whether the attribute size is longer than what we can put in the ATT
+                     PDU.
+            handle: The attribute handle.
+        """
+        uuid = self.get_uuid_from_handle(handle)
+        status = self.check_permissions(uuid)
+        value = []
+        if status == floss_enums.GattStatus.SUCCESS:
+            if not is_long:
+                offset = 0
+            status, value = self.generic_read(offset, handle)
+
+        self.proxy().SendResponse(self.server_id, addr, trans_id, status, offset, value)
+
+    def on_attr_write(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
+        """Handles the read request for GATT attribute.
+
+        Args:
+            addr: Remote device MAC address.
+            trans_id: Transaction id.
+            offset: Represents the offset at which the attribute value should be written.
+            length: The length of the attribute value that should be written.
+            is_prep: A boolean value representing whether it's a 'prepare write' command.
+            need_rsp: A boolean value representing whether it's a 'write no response' command.
+            handle: The attribute handle.
+            value: The value that should be written to the attribute.
+        """
+        uuid = self.get_uuid_from_handle(handle)
+        status = self.check_permissions(uuid)
+
+        if status == floss_enums.GattStatus.SUCCESS:
+            if is_prep:
+                self.write_requests.append((addr, trans_id, offset, length, is_prep, need_rsp, handle, value))
+            else:
+                # write request.
+                status, value = self.generic_write(offset, length, handle, value)
+        else:
+            value = []
+
+        if need_rsp:
+            self.proxy().SendResponse(self.server_id, addr, trans_id, status, offset, value)
+        else:
+            logging.info('No need to send response.')
+
+    def __set_pts_attributes_value(self):
+        """Sets PTS attributes value."""
+        if not self.pts_set_values:
+            self.update_attribute_value(self.SERVICE_ATTR_UUID, self.SHORT_TEST_VALUE)
+            self.update_attribute_value(self.SERVER_CHANGED_CHAR_UUID, self.SHORT_TEST_VALUE)
+            self.update_attribute_value(self.SERVER_SUP_FEAT_UUID, self.SHORT_TEST_VALUE)
+            self.update_attribute_value(self.CLIENT_SUP_FEAT_UUID, self.SHORT_TEST_VALUE)
+            self.update_attribute_value(self.DATABASE_HASH_UUID, self.SHORT_TEST_VALUE)
+            self.update_attribute_value(self.LONG_CHAR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.DESCRIPTOR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.KEY_SIZE_CHAR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.UNAUTHORIZED_CHAR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.AUTHENTICATION_CHAR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.INVALID_FOR_LE_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.INVALID_FOR_BR_EDR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.NO_READ_CHAR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.NO_WRITE_CHAR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.SHORT_CHAR_UUID, self.SHORT_TEST_VALUE)
+            self.update_attribute_value(self.WRITE_NO_RESPONSE_CHAR_UUID, self.LONG_TEST_VALUE)
+            self.update_attribute_value(self.NOTIFY_CHAR_UUID, self.SHORT_TEST_VALUE)
+            self.update_attribute_value(self.AUTHENTICATE_SHORT_CHAR_UUID, self.SHORT_TEST_VALUE)
+            self.update_attribute_value(self.CCC_DESCRIPTOR_UUID, self.INDICATION_VALUE)
+            self.update_attribute_value(self.LONG_CHAR_512_UUID, self.LONG_TEST_VALUE_512)
+            self.update_attribute_value(self.MITM_SHORT_CHAR_UUID, self.SHORT_TEST_VALUE)
+            self.pts_set_values = True
+
+    def __define_services(self):
+        """Defines GATT services for PTS testing."""
+
+        service = bluetooth_gatt_service.Service()
+        characteristic = bluetooth_gatt_service.Characteristic()
+        descriptor = bluetooth_gatt_service.Descriptor()
+        service.included_services = []
+        service.characteristics = []
+        characteristic.descriptors = []
+
+        service.uuid = self.SERVICE_ATTR_UUID
+        service.instance_id = 0
+        service.service_type = self.BTGATT_DB_PRIMARY_SERVICE
+
+        characteristic.uuid = self.SERVER_CHANGED_CHAR_UUID
+        characteristic.instance_id = 1
+        characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_INDICATE
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+
+        descriptor.uuid = self.CCC_DESCRIPTOR_UUID
+        descriptor.instance_id = 2
+        descriptor.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.descriptors.append(descriptor)
+
+        service.characteristics.append(characteristic)
+
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.SERVER_SUP_FEAT_UUID
+        characteristic.instance_id = 3
+        characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.INVALID
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.CLIENT_SUP_FEAT_UUID
+        characteristic.instance_id = 4
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.INVALID
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.DATABASE_HASH_UUID
+        characteristic.instance_id = 5
+        characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.INVALID
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.DATABASE_HASH_UUID
+        characteristic.instance_id = 5
+        characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.INVALID
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        descriptor = bluetooth_gatt_service.Descriptor()
+        characteristic.uuid = self.LONG_CHAR_UUID
+        characteristic.instance_id = 6
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        descriptor.uuid = self.DESCRIPTOR_UUID
+        descriptor.instance_id = 7
+        descriptor.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.descriptors.append(descriptor)
+        service.characteristics.append(characteristic)
+
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.KEY_SIZE_CHAR_UUID
+        characteristic.instance_id = 8
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+        characteristic.permissions = (
+            10 << 12) | floss_enums.GattPermission.PERM_READ_ENCRYPTED | floss_enums.GattPermission.PERM_WRITE_ENCRYPTED
+        characteristic.key_size = 10
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.UNAUTHORIZED_CHAR_UUID
+        characteristic.instance_id = 9
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.AUTHENTICATION_CHAR_UUID
+        characteristic.instance_id = 10
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_AUTH)
+        characteristic.permissions = self.GATT_READ_AUTH_REQUIRED | self.GATT_WRITE_AUTH_REQUIRED
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.INVALID_FOR_LE_UUID
+        characteristic.instance_id = 11
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.INVALID_FOR_BR_EDR_UUID
+        characteristic.instance_id = 12
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.NO_READ_CHAR_UUID
+        characteristic.instance_id = 13
+        # Only write property.
+        characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE
+        # Only write permission.
+        characteristic.permissions = floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.NO_WRITE_CHAR_UUID
+        characteristic.instance_id = 14
+        # Only read property.
+        characteristic.properties = floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ
+        # Only read permission.
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.INVALID
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.SHORT_CHAR_UUID
+        characteristic.instance_id = 15
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.WRITE_NO_RESPONSE_CHAR_UUID
+        characteristic.instance_id = 16
+        # Write without response property.
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE_NR)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE_NO_RSP
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        descriptor = bluetooth_gatt_service.Descriptor()
+        characteristic.uuid = self.NOTIFY_CHAR_UUID
+        characteristic.instance_id = 17
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_NOTIFY |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_INDICATE)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        descriptor.uuid = self.CCC_DESCRIPTOR_UUID
+        descriptor.instance_id = 18
+        descriptor.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+
+        characteristic.descriptors.append(descriptor)
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.AUTHENTICATE_SHORT_CHAR_UUID
+        characteristic.instance_id = 19
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_AUTH)
+        characteristic.permissions = self.GATT_READ_AUTH_REQUIRED | self.GATT_WRITE_AUTH_REQUIRED
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.LONG_CHAR_512_UUID
+        characteristic.instance_id = 20
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE)
+        characteristic.permissions = floss_enums.GattPermission.PERM_READ | floss_enums.GattPermission.PERM_WRITE
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        characteristic = bluetooth_gatt_service.Characteristic()
+        characteristic.uuid = self.MITM_SHORT_CHAR_UUID
+        characteristic.instance_id = 21
+        characteristic.properties = (floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_READ |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_WRITE |
+                                     floss_enums.GattCharacteristicProprieties.CHAR_PROP_BIT_AUTH)
+        characteristic.permissions = self.GATT_READ_MITM_REQUIRED | self.GATT_WRITE_MITM_REQUIRED
+        characteristic.key_size = 0
+        characteristic.write_type = floss_enums.GattWriteType.WRITE
+        characteristic.descriptors = []
+        service.characteristics.append(characteristic)
+        # ----------------------------------------------------
+        service_dict = bluetooth_gatt_service.convert_object_to_dict(service)
+        self.proxy().AddService(self.server_id, self.make_dbus_service(service_dict))
+
     @utils.glib_callback()
     def on_server_registered(self, status, server_id):
         """Handles server registration callback.
@@ -530,6 +1097,7 @@
             logging.error('Failed to register server with id: %s, status = %s', server_id, status)
             return
         self.server_id = server_id
+        self.__define_services()
 
     @utils.glib_callback()
     def on_server_connection_state(self, server_id, connected, addr):
@@ -551,8 +1119,15 @@
             status: floss_enums.GattStatus.
             service: BluetoothGattService.
         """
+        if status != floss_enums.GattStatus.SUCCESS:
+            return
+        self.gatt_services.append(bluetooth_gatt_service.create_gatt_service(service))
+
         logging.debug('on_service_added: status: %s, service: %s', status, service)
 
+        # This function will run once when the server is initiated.
+        self.__set_pts_attributes_value()
+
     @utils.glib_callback()
     def on_service_removed(self, status, handle):
         """Handles service removed callback.
@@ -579,6 +1154,8 @@
             'on_characteristic_read_request: device address: %s, trans_id: %s, offset: %s, is_long: %s, handle: %s',
             addr, trans_id, offset, is_long, handle)
 
+        self.on_attr_read(addr, trans_id, offset, is_long, handle)
+
     @utils.glib_callback()
     def on_descriptor_read_request(self, addr, trans_id, offset, is_long, handle):
         """Handles the read request of the descriptor callback.
@@ -594,42 +1171,45 @@
         logging.debug(
             'on_descriptor_read_request: device address: %s, trans_id: %s, offset: %s, is_long: %s, handle: %s', addr,
             trans_id, offset, is_long, handle)
+        self.on_attr_read(addr, trans_id, offset, is_long, handle)
 
     @utils.glib_callback()
-    def on_characteristic_write_request(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+    def on_characteristic_write_request(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
         """Handles the write request of the characteristic callback.
 
         Args:
             addr: Remote device MAC address.
             trans_id: Transaction id.
             offset: Represents the offset at which the attribute value should be written.
-            len: The length of the attribute value that should be written.
-            is_prep: A boolean value representing whether it's a "prepare write" command.
-            need_rsp: A boolean value representing whether it's a "write no response" command.
+            length: The length of the attribute value that should be written.
+            is_prep: A boolean value representing whether it's a 'prepare write' command.
+            need_rsp: A boolean value representing whether it's a 'write no response' command.
             handle: The characteristic handle.
             value: The value that should be written to the attribute.
         """
         logging.debug(
             'on_characteristic_write_request: device address: %s, trans_id: %s, offset: %s, length: %s, is_prep: %s, '
-            'need_rsp: %s, handle: %s, values: %s', addr, trans_id, offset, len, is_prep, need_rsp, handle, value)
+            'need_rsp: %s, handle: %s, values: %s', addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
+        self.on_attr_write(addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
 
     @utils.glib_callback()
-    def on_descriptor_write_request(self, addr, trans_id, offset, len, is_prep, need_rsp, handle, value):
+    def on_descriptor_write_request(self, addr, trans_id, offset, length, is_prep, need_rsp, handle, value):
         """Handles the write request of the descriptor callback.
 
         Args:
             addr: Remote device MAC address.
             trans_id: Transaction id.
             offset: The offset value at which the value should be written.
-            len: The length of the value that should be written.
-            is_prep: A boolean value representing whether it's a "prepare write" command.
-            need_rsp: A boolean value representing whether it's a "write no response" command.
+            length: The length of the value that should be written.
+            is_prep: A boolean value representing whether it's a 'prepare write' command.
+            need_rsp: A boolean value representing whether it's a 'write no response' command.
             handle: The descriptor handle.
             value: The value that should be written to the descriptor.
         """
         logging.debug(
             'on_descriptor_write_request: device address: %s, trans_id: %s, offset: %s, length: %s, is_prep: %s, '
-            'need_rsp: %s, handle: %s, values: %s', addr, trans_id, offset, len, is_prep, need_rsp, handle, value)
+            'need_rsp: %s, handle: %s, values: %s', addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
+        self.on_attr_write(addr, trans_id, offset, length, is_prep, need_rsp, handle, value)
 
     @utils.glib_callback()
     def on_execute_write(self, addr, trans_id, exec_write):
@@ -642,6 +1222,19 @@
         """
         logging.debug('on_execute_write: device address: %s, trans_id: %s, exec_write: %s', addr, trans_id, exec_write)
 
+        if not exec_write:
+            self.write_requests.clear()
+            status = floss_enums.GattStatus.SUCCESS
+        else:
+            write_requests, self.write_requests = self.write_requests, collections.deque([])
+            for request in write_requests:
+                _, _, offset2, length, _, _, handle2, value2 = request
+                status, _ = self.generic_write(offset2, length, handle2, value2)
+                if status != floss_enums.GattStatus.SUCCESS:
+                    break
+
+        self.proxy().SendResponse(self.server_id, addr, trans_id, status, 0, [])
+
     @utils.glib_callback()
     def on_notification_sent(self, addr, status):
         """Handles notification sent callback.
@@ -661,6 +1254,7 @@
             mtu: Maximum transmission unit.
         """
         logging.debug('on_mtu_changed: device address: %s, mtu : %s', addr, mtu)
+        self.mtu_value = mtu
 
     @utils.glib_callback()
     def on_phy_update(self, addr, tx_phy, rx_phy, status):
@@ -898,15 +1492,16 @@
         """Makes struct for descriptor D-Bus.
 
         Args:
-            uuid : Descriptor UUID as array of bytes.
+            uuid : Descriptor UUID as string.
             instance_id: Descriptor identifier.
             permissions: Descriptor permissions.
 
         Returns:
             Dictionary of descriptor.
         """
+        desc_uuid = utils.get_uuid_as_list(uuid)
         return {
-            'uuid': GLib.Variant('ay', uuid),
+            'uuid': GLib.Variant('ay', desc_uuid),
             'instance_id': GLib.Variant('i', instance_id),
             'permissions': GLib.Variant('i', permissions)
         }
@@ -915,7 +1510,7 @@
         """Makes struct for characteristic D-Bus.
 
         Args:
-            uuid : Characteristic UUID as array of bytes.
+            uuid : Characteristic UUID as string.
             instance_id: Characteristic handle id.
             properties: Characteristic properties.
             permissions: Characteristic permissions.
@@ -929,8 +1524,9 @@
         desc = []
         for d in descriptors:
             desc.append(self.make_dbus_descriptor(d['uuid'], d['instance_id'], d['permissions']))
+        char_uuid = utils.get_uuid_as_list(uuid)
         return {
-            'uuid': GLib.Variant('ay', uuid),
+            'uuid': GLib.Variant('ay', char_uuid),
             'instance_id': GLib.Variant('i', instance_id),
             'properties': GLib.Variant('i', properties),
             'permissions': GLib.Variant('i', permissions),
@@ -957,9 +1553,9 @@
         included_services = []
         for s in service['included_services']:
             included_services.append(self.make_dbus_service(s))
-
+        service_uuid = utils.get_uuid_as_list(service['uuid'])
         return {
-            'uuid': GLib.Variant('ay', service['uuid']),
+            'uuid': GLib.Variant('ay', service_uuid),
             'instance_id': GLib.Variant('i', service['instance_id']),
             'service_type': GLib.Variant('i', service['service_type']),
             'characteristics': GLib.Variant('aa{sv}', characteristics),
diff --git a/floss/pandora/floss/media_client.py b/floss/pandora/floss/media_client.py
index 6c42378..83dbe1e 100644
--- a/floss/pandora/floss/media_client.py
+++ b/floss/pandora/floss/media_client.py
@@ -182,6 +182,7 @@
         self.bus = bus
         self.hci = hci
         self.objpath = self.MEDIA_OBJECT_PATTERN.format(hci)
+        self.devices = []
 
         # We don't register callbacks by default.
         self.callbacks = None
@@ -198,6 +199,9 @@
             device: The struct of BluetoothAudioDevice.
         """
         logging.debug('on_bluetooth_audio_device_added: device: %s', device)
+        if device['address'] in self.devices:
+            logging.debug("Device already added")
+        self.devices.append(device['address'])
 
     @utils.glib_callback()
     def on_bluetooth_audio_device_removed(self, addr):
@@ -207,6 +211,8 @@
             addr: Address of device to be removed.
         """
         logging.debug('on_bluetooth_audio_device_removed: address: %s', addr)
+        if addr in self.devices:
+            self.devices.remove(addr)
 
     @utils.glib_callback()
     def on_absolute_volume_supported_changed(self, supported):
@@ -523,3 +529,23 @@
         """
         self.proxy().SetPlayerMetadata(metadata)
         return True
+
+    def register_callback_observer(self, name, observer):
+        """Adds an observer for all callbacks.
+
+        Args:
+            name: Name of the observer.
+            observer: Observer that implements all callback classes.
+        """
+        if isinstance(observer, BluetoothMediaCallbacks):
+            self.callbacks.add_observer(name, observer)
+
+    def unregister_callback_observer(self, name, observer):
+        """Removes an observer for all callbacks.
+
+        Args:
+            name: Name of the observer.
+            observer: Observer that implements all callback classes.
+        """
+        if isinstance(observer, BluetoothMediaCallbacks):
+            self.callbacks.remove_observer(name, observer)
diff --git a/floss/pandora/floss/sox_utils.py b/floss/pandora/floss/sox_utils.py
new file mode 100644
index 0000000..45d2b5c
--- /dev/null
+++ b/floss/pandora/floss/sox_utils.py
@@ -0,0 +1,89 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#     https://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.
+
+SOX_PATH = 'sox'
+
+
+def _raw_format_args(channels, bits, rate):
+    """Gets raw format args used in sox.
+
+    Args:
+        channels: Number of channels.
+        bits: Bit length for a sample.
+        rate: Sampling rate.
+
+    Returns:
+        A list of args.
+    """
+    args = ['-t', 'raw', '-e', 'signed']
+    args += _format_args(channels, bits, rate)
+    return args
+
+
+def _format_args(channels, bits, rate):
+    """Gets format args used in sox.
+
+    Args:
+        channels: Number of channels.
+        bits: Bit length for a sample.
+        rate: Sampling rate.
+
+    Returns:
+        A list of args.
+    """
+    return ['-c', str(channels), '-b', str(bits), '-r', str(rate)]
+
+
+def generate_sine_tone_cmd(filename,
+                           channels=2,
+                           bits=16,
+                           rate=48000,
+                           duration=None,
+                           frequencies=440,
+                           gain=None,
+                           vol=None,
+                           raw=True):
+    """Gets a command to generate sine tones at specified frequencies.
+
+    Args:
+        filename: The name of the file to store the sine wave in.
+        channels: The number of channels.
+        bits: The number of bits of each sample.
+        rate: The sampling rate.
+        duration: The length of the generated sine tone (in seconds).
+        frequencies: The frequencies of the sine wave. Pass a number or a list to specify frequency for each channel.
+        gain: The gain (in db).
+        vol: A float for volume scale used in sox command.
+             E.g. 1.0 is the same. 0.5 to scale volume by
+             half. -1.0 to invert the data.
+        raw: True to use raw data format. False to use what filename specifies.
+    """
+    args = [SOX_PATH, '-n']
+    if raw:
+        args += _raw_format_args(channels, bits, rate)
+    else:
+        args += _format_args(channels, bits, rate)
+    args.append(filename)
+    args.append('synth')
+    if duration is not None:
+        args.append(str(duration))
+    if not isinstance(frequencies, list):
+        frequencies = [frequencies]
+    for freq in frequencies:
+        args += ['sine', str(freq)]
+    if gain is not None:
+        args += ['gain', str(gain)]
+    if vol is not None:
+        args += ['vol', str(vol)]
+    return args
diff --git a/floss/pandora/floss/utils.py b/floss/pandora/floss/utils.py
index be825ed..bcd5feb 100644
--- a/floss/pandora/floss/utils.py
+++ b/floss/pandora/floss/utils.py
@@ -17,7 +17,9 @@
 import logging
 import threading
 import time
+import uuid
 from typing import List, Optional
+import traceback
 
 from floss.pandora.floss import floss_enums
 from gi.repository import GLib
@@ -76,6 +78,64 @@
         time.sleep(sleep_interval)
 
 
+def dbus_safe(default_return_value, return_error=False):
+    """Catches all DBus exceptions and return a default value instead.
+
+    Wrap a function with a try block that catches DBus exceptions and returns the error with the specified return
+    status. The exception is logged to aid in debugging.
+
+    If |return_error| is set, the call will return a tuple with (default_return_value, str(error)).
+
+    Args:
+        default_return_value: What value to return in case of errors.
+        return_error: Whether to return the error string as well.
+
+    Returns:
+        Either the return value from the method call if successful or the |default_return_value| or
+        a tuple(default_return_value, str(error)).
+    """
+
+    def decorator(wrapped_function):
+        """Calls a function and catch DBus errors.
+
+        Args:
+            wrapped_function: Function to call in dbus safe context.
+
+        Returns:
+            Function return value or default_return_value on failure.
+        """
+
+        @functools.wraps(wrapped_function)
+        def wrapper(*args, **kwargs):
+            """Passes args and kwargs to a dbus safe function.
+
+            Args:
+                args: Formal python arguments.
+                kwargs: Keyword python arguments.
+
+            Returns:
+                Function return value or default_return_value on failure.
+            """
+            logging.debug('%s()', wrapped_function.__name__)
+            try:
+                return wrapped_function(*args, **kwargs)
+            except GLib.Error as e:
+                logging.debug('Exception while performing operation %s: %s', wrapped_function.__name__, e)
+
+                if return_error:
+                    return (default_return_value, str(e))
+                else:
+                    return default_return_value
+            except Exception as e:
+                logging.debug('Exception in %s: %s', wrapped_function.__name__, e)
+                logging.debug(traceback.format_exc())
+                raise
+
+        return wrapper
+
+    return decorator
+
+
 def generate_dbus_cb_objpath(name, hci=None):
     """Generates a DBus callbacks object path with a suffix that won't conflict.
 
@@ -389,6 +449,18 @@
     return f'{uuid32}-0000-1000-8000-00805f9b34fb'
 
 
+def get_uuid_as_list(str_uuid):
+    """Converts string uuid to a list of bytes.
+
+    Args:
+        str_uuid: String UUID.
+
+    Returns:
+        UUID string as list of bytes.
+    """
+    return list(uuid.UUID(str_uuid).bytes)
+
+
 def advertise_data_from(request_data: host_pb2.DataTypes):
     """Mapping DataTypes to a dict.
 
diff --git a/floss/pandora/server/a2dp.py b/floss/pandora/server/a2dp.py
new file mode 100644
index 0000000..8ba66dc
--- /dev/null
+++ b/floss/pandora/server/a2dp.py
@@ -0,0 +1,254 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#     https://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.
+"""A2DP grpc interface."""
+
+import asyncio
+import json
+import logging
+from typing import AsyncGenerator
+from typing import AsyncIterator
+
+from floss.pandora.floss import audio_utils
+from floss.pandora.floss import cras_utils
+from floss.pandora.floss import media_client
+from floss.pandora.floss import utils
+from floss.pandora.server import bluetooth as bluetooth_module
+from google.protobuf import wrappers_pb2, empty_pb2
+import grpc
+from pandora import a2dp_grpc_aio
+from pandora import a2dp_pb2
+
+
+class A2DPService(a2dp_grpc_aio.A2DPServicer):
+    """Service to trigger Bluetooth A2DP procedures.
+
+    This class implements the Pandora bluetooth test interfaces,
+    where the meta class definition is automatically generated by the protobuf.
+    The interface definition can be found in:
+    https://cs.android.com/android/platform/superproject/+/main:external/pandora/bt-test-interfaces/pandora/a2dp.proto
+    """
+
+    def __init__(self, bluetooth: bluetooth_module.Bluetooth):
+        self.bluetooth = bluetooth
+        self._cras_test_client = cras_utils.CrasTestClient()
+        cras_utils.set_floss_enabled(True)
+
+    async def OpenSource(self, request: a2dp_pb2.OpenSourceRequest,
+                         context: grpc.ServicerContext) -> a2dp_pb2.OpenSourceResponse:
+
+        class ConnectionObserver(media_client.BluetoothMediaCallbacks):
+            """Observer to observe the A2DP profile connection state."""
+
+            def __init__(self, task):
+                self.task = task
+
+            @utils.glib_callback()
+            def on_bluetooth_audio_device_added(self, remote_device):
+                if remote_device['address'] != self.task['address']:
+                    return
+
+                future = self.task['open_source']
+                future.get_loop().call_soon_threadsafe(future.set_result, True)
+
+        connection = utils.connection_from(request.connection)
+        address = connection.address
+        connected_devices = self.bluetooth.get_connected_audio_devices()
+
+        if not self.bluetooth.is_connected(address) or address not in connected_devices:
+            try:
+                open_source = asyncio.get_running_loop().create_future()
+                observer = ConnectionObserver({'open_source': open_source, 'address': address})
+                name = utils.create_observer_name(observer)
+                self.bluetooth.media_client.register_callback_observer(name, observer)
+                self.bluetooth.connect_device(address)
+                success = await asyncio.wait_for(open_source, timeout=10)
+
+                if not success:
+                    await context.abort(grpc.StatusCode.UNKNOWN, f'Failed to connect to the address {address}.')
+            except asyncio.TimeoutError as e:
+                logging.error(f'OpenSource: timeout for waiting A2DP connection. {e}')
+            finally:
+                self.bluetooth.media_client.unregister_callback_observer(name, observer)
+
+        cookie = utils.address_to(address)
+        return a2dp_pb2.OpenSourceResponse(source=a2dp_pb2.Source(cookie=cookie))
+
+    async def OpenSink(self, request: a2dp_pb2.OpenSinkRequest,
+                       context: grpc.ServicerContext) -> a2dp_pb2.OpenSinkResponse:
+
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)  # type: ignore
+        context.set_details('Method not implemented!')  # type: ignore
+        raise NotImplementedError('Method not implemented!')
+
+    async def WaitSource(self, request: a2dp_pb2.WaitSourceRequest,
+                         context: grpc.ServicerContext) -> a2dp_pb2.WaitSourceResponse:
+
+        class ConnectionObserver(media_client.BluetoothMediaCallbacks):
+            """Observer to observe the A2DP profile connection state."""
+
+            def __init__(self, task):
+                self.task = task
+
+            @utils.glib_callback()
+            def on_bluetooth_audio_device_added(self, remote_device):
+                if remote_device['address'] != self.task['address']:
+                    return
+
+                future = self.task['wait_source']
+                future.get_loop().call_soon_threadsafe(future.set_result, address)
+
+        connection = utils.connection_from(request.connection)
+        address = connection.address
+        if not address:
+            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Request address field must be set.')
+
+        connected_devices = self.bluetooth.get_connected_audio_devices()
+        if not self.bluetooth.is_connected(address) or address not in connected_devices:
+            try:
+                wait_source = asyncio.get_running_loop().create_future()
+                observer = ConnectionObserver({'wait_source': wait_source, 'address': address})
+                name = utils.create_observer_name(observer)
+                self.bluetooth.media_client.register_callback_observer(name, observer)
+                await asyncio.wait_for(wait_source, timeout=10)
+            except asyncio.TimeoutError as e:
+                logging.error(f'WaitSource: timeout for waiting A2DP connection. {e}')
+            finally:
+                self.bluetooth.media_client.unregister_callback_observer(name, observer)
+
+        cookie = utils.address_to(address)
+        return a2dp_pb2.WaitSourceResponse(source=a2dp_pb2.Source(cookie=cookie))
+
+    async def WaitSink(self, request: a2dp_pb2.WaitSinkRequest,
+                       context: grpc.ServicerContext) -> a2dp_pb2.WaitSinkResponse:
+
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)  # type: ignore
+        context.set_details('Method not implemented!')  # type: ignore
+        raise NotImplementedError('Method not implemented!')
+
+    async def IsSuspended(self, request: a2dp_pb2.IsSuspendedRequest,
+                          context: grpc.ServicerContext) -> wrappers_pb2.BoolValue:
+
+        address = utils.address_from(request.target.cookie)
+        connected_audio_devices = self.bluetooth.get_connected_audio_devices()
+        if address not in connected_audio_devices:
+            await context.abort(grpc.StatusCode.FAILED_PRECONDITION,
+                                'A2dp device is not connected, cannot get suspend state')
+
+        is_suspended = cras_utils.get_active_stream_count() == 0
+        return wrappers_pb2.BoolValue(value=is_suspended)
+
+    async def Start(self, request: a2dp_pb2.StartRequest, context: grpc.ServicerContext) -> a2dp_pb2.StartResponse:
+
+        target = request.WhichOneof('target')
+        address = utils.address_from(request.target.cookie)
+        connected_audio_devices = self.bluetooth.get_connected_audio_devices()
+        if address not in connected_audio_devices:
+            await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'A2dp device is not connected, cannot start')
+
+        audio_data = json.dumps(audio_utils.A2DP_TEST_DATA)
+        audio_data = json.loads(audio_data)
+        audio_utils.generate_playback_file(audio_data)
+
+        if not audio_utils.select_audio_output_node():
+            await context.abort(grpc.StatusCode.UNKNOWN, 'Failed to select audio output node')
+
+        if target == 'source':
+            self._cras_test_client.start_playing_subprocess(audio_data['file'],
+                                                            channels=audio_data['channels'],
+                                                            rate=audio_data['rate'],
+                                                            duration=audio_data['duration'])
+        else:
+            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f'Invalid target type: {target}.')
+
+        return a2dp_pb2.StartResponse(started=empty_pb2.Empty())
+
+    async def Suspend(self, request: a2dp_pb2.SuspendRequest,
+                      context: grpc.ServicerContext) -> a2dp_pb2.SuspendResponse:
+
+        target = request.WhichOneof('target')
+        address = utils.address_from(request.target.cookie)
+        connected_audio_devices = self.bluetooth.get_connected_audio_devices()
+        if address not in connected_audio_devices:
+            await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'A2dp device is not connected, cannot suspend')
+
+        if cras_utils.get_active_stream_count() == 0:
+            await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'A2dp Device is already suspended, cannot suspend')
+
+        if target == 'source':
+            self._cras_test_client.stop_playing_subprocess()
+        else:
+            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f'Invalid target type: {target}.')
+
+        return a2dp_pb2.SuspendResponse(suspended=empty_pb2.Empty())
+
+    async def Close(self, request: a2dp_pb2.CloseRequest, context: grpc.ServicerContext) -> a2dp_pb2.CloseResponse:
+
+        class ConnectionObserver(media_client.BluetoothMediaCallbacks):
+            """Observer to observe the A2DP profile connection state."""
+
+            def __init__(self, task):
+                self.task = task
+
+            @utils.glib_callback()
+            def on_bluetooth_audio_device_removed(self, address):
+                if address != self.task['address']:
+                    return
+
+                future = self.task['close_stream']
+                future.get_loop().call_soon_threadsafe(future.set_result, address)
+
+        address = utils.address_from(request.target.cookie)
+        connected_audio_devices = self.bluetooth.get_connected_audio_devices()
+        if address not in connected_audio_devices:
+            await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'A2dp device is not connected, cannot close')
+
+        try:
+            close_stream = asyncio.get_running_loop().create_future()
+            observer = ConnectionObserver({'close_stream': close_stream, 'address': address})
+            name = utils.create_observer_name(observer)
+            self.bluetooth.media_client.register_callback_observer(name, observer)
+            self.bluetooth.disconnect_media(address)
+            await close_stream
+        finally:
+            self.bluetooth.media_client.unregister_callback_observer(name, observer)
+        return a2dp_pb2.CloseResponse()
+
+    async def GetAudioEncoding(self, request: a2dp_pb2.GetAudioEncodingRequest,
+                               context: grpc.ServicerContext) -> a2dp_pb2.GetAudioEncodingResponse:
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)  # type: ignore
+        context.set_details('Method not implemented!')  # type: ignore
+        raise NotImplementedError('Method not implemented!')
+
+    async def PlaybackAudio(self, request: AsyncIterator[a2dp_pb2.PlaybackAudioRequest],
+                            context: grpc.ServicerContext) -> a2dp_pb2.PlaybackAudioResponse:
+
+        audio_signals = request
+        logging.info('PlaybackAudio: Wait for audio signal...')
+
+        audio_signal = await utils.anext(audio_signals)
+        audio_data = audio_signal.data
+
+        if cras_utils.get_active_stream_count() == 0:
+            await context.abort(grpc.StatusCode.FAILED_PRECONDITION, 'Audio track is not started')
+
+        audio_utils.generate_playback_file_from_binary_data(audio_data)
+        audio_file = audio_utils.A2DP_PLAYBACK_DATA['file']
+        self._cras_test_client.play(audio_file)
+        return a2dp_pb2.PlaybackAudioResponse()
+
+    async def CaptureAudio(self, request: a2dp_pb2.CaptureAudioRequest,
+                           context: grpc.ServicerContext) -> AsyncGenerator[a2dp_pb2.CaptureAudioResponse, None]:
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)  # type: ignore
+        context.set_details('Method not implemented!')  # type: ignore
+        raise NotImplementedError('Method not implemented!')
diff --git a/floss/pandora/server/bluetooth.py b/floss/pandora/server/bluetooth.py
index 25d44d4..fd2d3d3 100644
--- a/floss/pandora/server/bluetooth.py
+++ b/floss/pandora/server/bluetooth.py
@@ -19,13 +19,14 @@
 
 from floss.pandora.floss import adapter_client
 from floss.pandora.floss import advertising_client
+from floss.pandora.floss import floss_enums
+from floss.pandora.floss import gatt_client
+from floss.pandora.floss import gatt_server
 from floss.pandora.floss import manager_client
+from floss.pandora.floss import media_client
 from floss.pandora.floss import qa_client
 from floss.pandora.floss import scanner_client
 from floss.pandora.floss import socket_manager
-from floss.pandora.floss import gatt_client
-from floss.pandora.floss import gatt_server
-from floss.pandora.floss import floss_enums
 from floss.pandora.floss import utils
 from gi.repository import GLib
 import pydbus
@@ -66,6 +67,7 @@
         self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, self.DEFAULT_ADAPTER)
         self.scanner_client = scanner_client.FlossScannerClient(self.bus, self.DEFAULT_ADAPTER)
         self.qa_client = qa_client.FlossQAClient(self.bus, self.DEFAULT_ADAPTER)
+        self.media_client = media_client.FlossMediaClient(self.bus, self.DEFAULT_ADAPTER)
         self.gatt_client = gatt_client.FlossGattClient(self.bus, self.DEFAULT_ADAPTER)
         self.gatt_server = gatt_server.FlossGattServer(self.bus, self.DEFAULT_ADAPTER)
         self.socket_manager = socket_manager.FlossSocketManagerClient(self.bus, self.DEFAULT_ADAPTER)
@@ -133,6 +135,9 @@
         if not self.qa_client.register_qa_callback():
             logging.error('qa_client: Failed to register callbacks')
             return False
+        if not self.media_client.register_callback():
+            logging.error('media_client: Failed to register callbacks')
+            return False
         if not self.gatt_client.register_client(self.FAKE_GATT_APP_ID, False):
             logging.error('gatt_client: Failed to register callbacks')
             return False
@@ -153,6 +158,7 @@
             self.advertising_client.has_proxy(),
             self.scanner_client.has_proxy(),
             self.qa_client.has_proxy(),
+            self.media_client.has_proxy(),
             self.gatt_client.has_proxy(),
             self.gatt_server.has_proxy(),
             self.socket_manager.has_proxy()
@@ -189,6 +195,7 @@
             self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, default_adapter)
             self.scanner_client = scanner_client.FlossScannerClient(self.bus, default_adapter)
             self.qa_client = qa_client.FlossQAClient(self.bus, default_adapter)
+            self.media_client = media_client.FlossMediaClient(self.bus, default_adapter)
             self.gatt_client = gatt_client.FlossGattClient(self.bus, default_adapter)
             self.gatt_server = gatt_server.FlossGattServer(self.bus, default_adapter)
             self.socket_manager = socket_manager.FlossSocketManagerClient(self.bus, default_adapter)
@@ -397,3 +404,9 @@
 
     def close_socket(self, socket_id):
         return self.socket_manager.close(socket_id)
+
+    def get_connected_audio_devices(self):
+        return self.media_client.devices
+
+    def disconnect_media(self, address):
+        return self.media_client.disconnect(address)
diff --git a/floss/pandora/server/gatt.py b/floss/pandora/server/gatt.py
index 559ef9a..9cdf78e 100644
--- a/floss/pandora/server/gatt.py
+++ b/floss/pandora/server/gatt.py
@@ -111,7 +111,7 @@
             self.bluetooth.configure_mtu(address, request.mtu)
             status = await configure_mtu
             if status != floss_enums.GattStatus.SUCCESS:
-                raise RuntimeError('Failed to configure MTU.')
+                await context.abort(grpc.StatusCode.INTERNAL, 'Failed to configure MTU.')
         finally:
             self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
         return gatt_pb2.ExchangeMTUResponse()
@@ -255,7 +255,7 @@
 
             services, status = await search_services
             if status != floss_enums.GattStatus.SUCCESS:
-                raise RuntimeError('Failed to find services.')
+                await context.abort(grpc.StatusCode.INTERNAL, 'Failed to find services.')
             response = gatt_pb2.DiscoverServicesResponse()
             for serv in services:
                 response.services.append(self.create_gatt_service(serv))
@@ -295,8 +295,7 @@
 
                 status = self.bluetooth.fetch_remote(address)
                 if not status:
-                    raise RuntimeError(f'Failed to fetch remote device {address} '
-                                       f'uuids.')
+                    await context.abort(grpc.StatusCode.INTERNAL, f'Failed to fetch remote device {address} uuids.')
                 await device_uuids_changed
                 uuids = self.bluetooth.get_remote_uuids(address)
             response = gatt_pb2.DiscoverServicesSdpResponse()
@@ -335,7 +334,7 @@
             self.bluetooth.refresh_device(address)
             status = await refresh
             if status != floss_enums.GattStatus.SUCCESS:
-                raise RuntimeError('Failed to clear cache.')
+                await context.abort(grpc.StatusCode.INTERNAL, 'Failed to clear cache.')
         finally:
             self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
         return gatt_pb2.ClearCacheResponse()
@@ -370,8 +369,6 @@
             self.bluetooth.gatt_client.register_callback_observer(name, observer)
             self.bluetooth.read_characteristic(address, request.handle, self.AUTHENTICATION_NONE)
             value, status = await characteristic_from_handle
-            if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Found no characteristic with supported handle.')
         finally:
             self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
 
@@ -407,8 +404,6 @@
             def on_characteristic_read(self, addr, status, handle, value):
                 if addr != self.task['address']:
                     return
-                if handle < self.task['start_handle'] or handle > self.task['end_handle']:
-                    return
                 if floss_enums.GattStatus(status) != floss_enums.GattStatus.SUCCESS:
                     logging.error('Failed to read characteristic from handle. Status: %s', status)
                 future = self.task['characteristic_from_uuid']
@@ -427,8 +422,7 @@
             self.bluetooth.discover_services(address)
             services, status = await search_services
             if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Found no services.')
-
+                await context.abort(grpc.StatusCode.INTERNAL, 'Found no services.')
             characteristic_from_uuid = asyncio.get_running_loop().create_future()
             observer = ReadCharacteristicsFromUuidObserver({
                 'characteristic_from_uuid': characteristic_from_uuid,
@@ -449,27 +443,26 @@
                                                                       characteristic['instance_id'],
                                                                       self.AUTHENTICATION_NONE)
                         status, handle, value = await characteristic_from_uuid
-                        if status != floss_enums.GattStatus.SUCCESS:
-                            raise ValueError(f'Found no characteristic from uuid={request.uuid}.')
                         characteristics.append((status, handle, value))
+            if not characteristics:
+                self.bluetooth.read_using_characteristic_uuid(address, request.uuid, request.start_handle,
+                                                              request.end_handle, self.AUTHENTICATION_NONE)
+                status, handle, value = await characteristic_from_uuid
+                result = gatt_pb2.ReadCharacteristicsFromUuidResponse(characteristics_read=[
+                    gatt_pb2.ReadCharacteristicResponse(value=gatt_pb2.AttValue(value=bytes(value), handle=handle),
+                                                        status=status)
+                ])
+            else:
+                result = gatt_pb2.ReadCharacteristicsFromUuidResponse(characteristics_read=[
+                    gatt_pb2.ReadCharacteristicResponse(
+                        value=gatt_pb2.AttValue(value=bytes(value), handle=handle),
+                        status=status,
+                    ) for status, handle, value in characteristics
+                ])
         finally:
             for name, observer in observers:
                 self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
-
-        if not characteristics:
-            return gatt_pb2.ReadCharacteristicsFromUuidResponse(characteristics_read=[
-                gatt_pb2.ReadCharacteristicResponse(
-                    value=gatt_pb2.AttValue(value=bytes(), handle=request.start_handle),
-                    status=gatt_pb2.UNKNOWN_ERROR,
-                )
-            ])
-
-        return gatt_pb2.ReadCharacteristicsFromUuidResponse(characteristics_read=[
-            gatt_pb2.ReadCharacteristicResponse(
-                value=gatt_pb2.AttValue(value=bytes(value), handle=handle),
-                status=status,
-            ) for status, handle, value in characteristics
-        ])
+        return result
 
     async def ReadCharacteristicDescriptorFromHandle(
             self, request: gatt_pb2.ReadCharacteristicDescriptorRequest,
@@ -502,8 +495,6 @@
             self.bluetooth.gatt_client.register_callback_observer(name, observer)
             self.bluetooth.read_descriptor(address, request.handle, self.AUTHENTICATION_NONE)
             value, status = await descriptor
-            if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Found no descriptor with supported handle.')
         finally:
             self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
 
@@ -530,7 +521,7 @@
         def convert_req_to_dictionary(request):
             service_dict = {
                 'service_type': self.SERVICE_TYPE_PRIMARY,
-                'uuid': list(UUID(request.uuid).bytes),
+                'uuid': request.uuid,
                 'instance_id': self.DEFAULT_INSTANCE_ID,
                 'included_services': [],
                 'characteristics': [],
@@ -539,7 +530,7 @@
             # Iterate through the characteristics in the request.
             for char in request.characteristics:
                 char_dict = {
-                    'uuid': list(UUID(char.uuid).bytes),
+                    'uuid': char.uuid,
                     'instance_id': self.DEFAULT_INSTANCE_ID,
                     'properties': char.properties,
                     'permissions': char.permissions,
@@ -551,7 +542,7 @@
                 # Iterate through the descriptors in the characteristic.
                 for desc in char.descriptors:
                     desc_dict = {
-                        'uuid': list(UUID(desc.uuid).bytes),
+                        'uuid': desc.uuid,
                         'instance_id': self.DEFAULT_INSTANCE_ID,
                         'permissions': desc.permissions,
                     }
@@ -569,7 +560,7 @@
             self.bluetooth.add_service(serv_dic)
             status, service = await register_service
             if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Failed to register service.')
+                await context.abort(grpc.StatusCode.INTERNAL, 'Failed to register service.')
         finally:
             self.bluetooth.gatt_server.unregister_callback_observer(name, observer)
 
@@ -634,7 +625,7 @@
             self.bluetooth.read_descriptor(address, request.handle, self.AUTHENTICATION_NONE)
             value, status = await descriptor_futures['read_descriptor']
             if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Found no descriptor with supported handle.')
+                await context.abort(grpc.StatusCode.INTERNAL, 'Found no descriptor with supported handle.')
 
             search_services = asyncio.get_running_loop().create_future()
             observer = DiscoveryObserver({'search_services': search_services, 'address': address})
@@ -644,7 +635,7 @@
             self.bluetooth.discover_services(address)
             services, status = await search_services
             if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Found no device services.')
+                await context.abort(grpc.StatusCode.INTERNAL, 'Found no device services.')
 
             characteristic_handle = None
             for serv in services:
@@ -665,8 +656,6 @@
                 self.bluetooth.write_descriptor(address, request.handle, self.AUTHENTICATION_NONE,
                                                 self.ENABLE_NOTIFICATION_VALUE)
             status = await descriptor_futures['write_descriptor']
-            if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Can not write descriptor.')
         finally:
             for name, observer in observers:
                 self.bluetooth.gatt_client.unregister_callback_observer(name, observer)
@@ -722,7 +711,7 @@
             self.bluetooth.read_descriptor(address, request.handle, self.AUTHENTICATION_NONE)
             value, status = await read_descriptor
             if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Found no descriptor with supported handle.')
+                await context.abort(grpc.StatusCode.INTERNAL, 'Found no descriptor with supported handle.')
 
             search_services = asyncio.get_running_loop().create_future()
             observer = DiscoveryObserver({'search_services': search_services, 'address': address})
@@ -732,7 +721,7 @@
             self.bluetooth.discover_services(address)
             services, status = await search_services
             if status != floss_enums.GattStatus.SUCCESS:
-                raise ValueError('Found no device services.')
+                await context.abort(grpc.StatusCode.INTERNAL, 'Found no device services.')
 
             characteristic_handle = None
             for serv in services:
diff --git a/floss/pandora/server/server.py b/floss/pandora/server/server.py
index e0c0445..3c58f30 100644
--- a/floss/pandora/server/server.py
+++ b/floss/pandora/server/server.py
@@ -16,6 +16,7 @@
 import asyncio
 import logging
 
+from floss.pandora.server import a2dp
 from floss.pandora.server import bluetooth as bluetooth_module
 from floss.pandora.server import gatt
 from floss.pandora.server import hid
@@ -24,6 +25,7 @@
 from floss.pandora.server import rfcomm
 from floss.pandora.server import security
 import grpc
+from pandora import a2dp_grpc_aio
 from pandora import host_grpc_aio
 from pandora import security_grpc_aio
 from pandora_experimental import gatt_grpc_aio
@@ -41,7 +43,7 @@
         while True:
             bluetooth = bluetooth_module.Bluetooth()
             bluetooth.reset()
-            logging.info("bluetooth initialized")
+            logging.info('bluetooth initialized')
 
             server = grpc.aio.server()
             security_service = security.SecurityService(bluetooth)
@@ -65,10 +67,13 @@
             rfcomm_service = rfcomm.RFCOMMService(bluetooth)
             rfcomm_grpc_aio.add_RFCOMMServicer_to_server(rfcomm_service, server)
 
+            a2dp_service = a2dp.A2DPService(bluetooth)
+            a2dp_grpc_aio.add_A2DPServicer_to_server(a2dp_service, server)
+
             server.add_insecure_port(f'[::]:{port}')
 
             await server.start()
-            logging.info("server started")
+            logging.info('server started')
 
             await server.wait_for_termination()
             bluetooth.cleanup()
diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java
index 9f0f623..e7a3a94 100644
--- a/framework/java/android/bluetooth/BluetoothPbapClient.java
+++ b/framework/java/android/bluetooth/BluetoothPbapClient.java
@@ -218,7 +218,7 @@
                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
             }
         }
-        return true;
+        return false;
     }
 
     /**
diff --git a/framework/tests/bumble/AndroidTest.xml b/framework/tests/bumble/AndroidTest.xml
index 5f56a28..089d771 100644
--- a/framework/tests/bumble/AndroidTest.xml
+++ b/framework/tests/bumble/AndroidTest.xml
@@ -32,10 +32,7 @@
         <option name="throw-if-cmd-fail" value="true" />
         <option name="run-command" value="setprop persist.bluetooth.btsnooplogmode full" />
         <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" />
-        <option name="run-command"
-                value="device_config put bluetooth INIT_logging_debug_enabled_for_all true" />
-        <option name="run-command"
-                value="device_config put bluetooth INIT_default_log_level_str LOG_VERBOSE" />
+        <option name="run-command" value="setprop log.tag.bluetooth VERBOSE" />
         <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
         <option name="run-command" value="cmd bluetooth_manager disable" />
         <option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_OFF" />
diff --git a/system/blueberry/tests/gd_sl4a/lib/gd_sl4a_base_test.py b/system/blueberry/tests/gd_sl4a/lib/gd_sl4a_base_test.py
index d673adc..a3aed2a 100644
--- a/system/blueberry/tests/gd_sl4a/lib/gd_sl4a_base_test.py
+++ b/system/blueberry/tests/gd_sl4a/lib/gd_sl4a_base_test.py
@@ -74,8 +74,8 @@
 
     def setup_test(self):
         self.cert.rootservice.StartStack(
-            facade_rootservice.StartStackRequest(
-                module_under_test=facade_rootservice.BluetoothModule.Value(self.cert_module),))
+            facade_rootservice.StartStackRequest(module_under_test=facade_rootservice.BluetoothModule.Value(
+                self.cert_module),))
         self.cert.wait_channel_ready()
 
         self.timer_list = []
@@ -87,7 +87,7 @@
         self.dut.sl4a.bluetoothDisableBLE()
         disable_bluetooth(self.dut.sl4a, self.dut.ed)
         # Enable full verbose logging for Bluetooth
-        self.dut.adb.shell("device_config put bluetooth INIT_logging_debug_enabled_for_all true")
+        self.dut.adb.shell("setprop log.tag.bluetooth VERBOSE")
         # Then enable Bluetooth
         enable_bluetooth(self.dut.sl4a, self.dut.ed)
         self.dut.sl4a.bluetoothDisableBLE()
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py b/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
index 0184288..d2bd77f 100644
--- a/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
+++ b/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
@@ -78,14 +78,14 @@
         self.dut.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
         getprop_result = self.dut.adb.getprop("persist.bluetooth.btsnooplogmode")
         if getprop_result is None or ("full" not in getprop_result.lower()):
-            self.dut.log.warning("Failed to enable Bluetooth HCI Snoop Logging on DUT, mode is {}"
-                                 .format(getprop_result))
+            self.dut.log.warning(
+                "Failed to enable Bluetooth HCI Snoop Logging on DUT, mode is {}".format(getprop_result))
         self.cert.adb.root()
         self.cert.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
         getprop_result = self.cert.adb.getprop("persist.bluetooth.btsnooplogmode")
         if getprop_result is None or ("full" not in getprop_result.lower()):
-            self.cert.log.warning("Failed to enable Bluetooth HCI Snoop Logging on CERT, mode is {}"
-                                  .format(getprop_result))
+            self.cert.log.warning(
+                "Failed to enable Bluetooth HCI Snoop Logging on CERT, mode is {}".format(getprop_result))
 
         self.ble = BleLib(dut=self.dut)
 
@@ -103,7 +103,7 @@
         device.sl4a.bluetoothDisableBLE()
         disable_bluetooth(device.sl4a, device.ed)
         # Enable full verbose logging for Bluetooth
-        device.adb.shell("device_config put bluetooth INIT_logging_debug_enabled_for_all true")
+        device.adb.shell("setprop log.tag.bluetooth VERBOSE")
         # Then enable Bluetooth
         enable_bluetooth(device.sl4a, device.ed)
         device.sl4a.bluetoothDisableBLE()
diff --git a/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py b/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py
index 8dc9e74..9828269 100644
--- a/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py
+++ b/system/blueberry/tests/sl4a_sl4a/security/irk_rotation_test.py
@@ -98,7 +98,7 @@
         self.cert.sl4a.bluetoothDisableBLE()
         disable_bluetooth(self.cert.sl4a, self.cert.ed)
         self.cert.adb.shell("setprop bluetooth.core.gap.le.privacy.enabled false")
-        self.cert.adb.shell("device_config put bluetooth INIT_logging_debug_enabled_for_all true")
+        self.cert.adb.shell("setprop log.tag.bluetooth VERBOSE")
         enable_bluetooth(self.cert.sl4a, self.cert.ed)
         self.cert.sl4a.bluetoothDisableBLE()
         self._test_le_reconnect_after_irk_rotation(False)
diff --git a/system/bta/ag/bta_ag_at.cc b/system/bta/ag/bta_ag_at.cc
index df7acbd..6621e22 100644
--- a/system/bta/ag/bta_ag_at.cc
+++ b/system/bta/ag/bta_ag_at.cc
@@ -25,9 +25,11 @@
 
 #include "bta/ag/bta_ag_at.h"
 
+#include <android_bluetooth_flags.h>
 #include <bluetooth/log.h>
 
 #include <cstdint>
+#include <cstdlib>
 
 #include "bta/ag/bta_ag_int.h"
 #include "bta/include/utl.h"
@@ -138,7 +140,31 @@
       /* if it's a set integer check max, min range */
       if (arg_type == BTA_AG_AT_SET &&
           p_cb->p_at_tbl[idx].fmt == BTA_AG_AT_INT) {
-        int_arg = utl_str2int(p_arg);
+#if TARGET_FLOSS
+        if (true)
+#else
+        if (IS_FLAG_ENABLED(bta_ag_cmd_brsf_allow_uint32))
+#endif
+        {
+          if (p_cb->p_at_tbl[idx].command_id == BTA_AG_LOCAL_EVT_BRSF) {
+            // Per HFP v1.9 BRSF could be 32-bit integer and we should ignore
+            // all reserved bits rather than responding ERROR.
+            long long int_arg_ll = std::atoll(p_arg);
+            if (int_arg_ll >= (1ll << 32) || int_arg_ll < 0) int_arg_ll = -1;
+
+            // Ignore reserved bits. 0xfff because there are 12 defined bits.
+            if (int_arg_ll > 0 && (int_arg_ll & (~0xfffll))) {
+              log::warn("BRSF: reserved bit is set: 0x{:x}", int_arg_ll);
+              int_arg_ll &= 0xfffll;
+            }
+
+            int_arg = static_cast<int16_t>(int_arg_ll);
+          } else {
+            int_arg = utl_str2int(p_arg);
+          }
+        } else {
+          int_arg = utl_str2int(p_arg);
+        }
         if (int_arg < (int16_t)p_cb->p_at_tbl[idx].min ||
             int_arg > (int16_t)p_cb->p_at_tbl[idx].max) {
           /* arg out of range; error */
diff --git a/system/bta/ag/bta_ag_cmd.cc b/system/bta/ag/bta_ag_cmd.cc
index bf63c95..9b8cd47 100644
--- a/system/bta/ag/bta_ag_cmd.cc
+++ b/system/bta/ag/bta_ag_cmd.cc
@@ -63,17 +63,6 @@
 
 #define COLON_IDX_4_VGSVGM 4
 
-/* Local events which will not trigger a higher layer callback */
-enum {
-  BTA_AG_LOCAL_EVT_FIRST = 0x100,
-  BTA_AG_LOCAL_EVT_CCWA,
-  BTA_AG_LOCAL_EVT_CLIP,
-  BTA_AG_LOCAL_EVT_CMER,
-  BTA_AG_LOCAL_EVT_BRSF,
-  BTA_AG_LOCAL_EVT_CMEE,
-  BTA_AG_LOCAL_EVT_BCC,
-};
-
 /* AT command interpreter table for HSP */
 static const tBTA_AG_AT_CMD bta_ag_hsp_cmd[] = {
     {"+CKPD", BTA_AG_AT_CKPD_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 200, 200},
diff --git a/system/bta/ag/bta_ag_int.h b/system/bta/ag/bta_ag_int.h
index 5429fce..5da5163 100644
--- a/system/bta/ag/bta_ag_int.h
+++ b/system/bta/ag/bta_ag_int.h
@@ -94,6 +94,17 @@
   BTA_AG_MAX_EVT,
 };
 
+/* Local events which will not trigger a higher layer callback */
+enum {
+  BTA_AG_LOCAL_EVT_FIRST = 0x100,
+  BTA_AG_LOCAL_EVT_CCWA,
+  BTA_AG_LOCAL_EVT_CLIP,
+  BTA_AG_LOCAL_EVT_CMER,
+  BTA_AG_LOCAL_EVT_BRSF,
+  BTA_AG_LOCAL_EVT_CMEE,
+  BTA_AG_LOCAL_EVT_BCC,
+};
+
 /* Actions to perform after a SCO event */
 enum {
   BTA_AG_POST_SCO_NONE,      /* no action */
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index 6fcee05..a06494d 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -697,7 +697,7 @@
   }
 
   void CleanUp() {
-    LOG_DEBUG();
+    log::debug("");
 
     BTA_GATTC_AppDeregister(gatt_if_);
     for (auto& device : devices_) {
@@ -2047,7 +2047,7 @@
         return;
       }
 
-      LOG_VERBOSE();
+      log::verbose("");
 
       const std::list<gatt::Service>* all_services =
           BTA_GATTC_GetServices(device->conn_id);
diff --git a/system/bta/include/bta_ras_api.h b/system/bta/include/bta_ras_api.h
index b8c54ea..fe62996 100644
--- a/system/bta/include/bta_ras_api.h
+++ b/system/bta/include/bta_ras_api.h
@@ -24,19 +24,12 @@
 namespace bluetooth {
 namespace ras {
 
-enum ProcedureDoneStatus : uint8_t {
-  ALL_RESULTS_COMPLETE = 0x0,
-  PARTIAL_RESULTS = 0x1,
-  ABORTED = 0xf,
-};
-
 class RasServer {
  public:
   virtual ~RasServer() = default;
   virtual void Initialize() = 0;
   virtual void PushProcedureData(RawAddress address, uint16_t procedure_count,
-                                 ProcedureDoneStatus procedure_done_status,
-                                 std::vector<uint8_t> data) = 0;
+                                 bool is_last, std::vector<uint8_t> data) = 0;
 };
 
 RasServer* GetRasServer();
diff --git a/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
index 4b8ad5a..935e2e3 100644
--- a/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
@@ -93,7 +93,7 @@
     return false;
   }
 
-  LOG_INFO();
+  log::info("");
   le_audio_source_hal_state = HAL_STOPPED;
   return true;
 }
@@ -104,7 +104,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   if (halSourceInterface_) {
     halSourceInterface_->Cleanup();
 
@@ -219,7 +219,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
 
   halSourceInterface_->StopSession();
   le_audio_source_hal_state = HAL_STOPPED;
@@ -256,7 +256,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   if (IS_FLAG_ENABLED(leaudio_start_stream_race_fix)) {
     halSourceInterface_->ConfirmStreamingRequestV2();
   } else {
@@ -271,7 +271,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSourceInterface_->SuspendedForReconfiguration();
 }
 
@@ -282,7 +282,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSourceInterface_->ReconfigurationComplete();
 }
 
@@ -293,7 +293,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   if (IS_FLAG_ENABLED(leaudio_start_stream_race_fix)) {
     halSourceInterface_->CancelStreamingRequestV2();
   } else {
@@ -308,7 +308,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSourceInterface_->SetRemoteDelay(remote_delay_ms);
 }
 
@@ -320,7 +320,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSourceInterface_->UpdateAudioConfigToHal(config);
 }
 }  // namespace
@@ -334,7 +334,7 @@
     return nullptr;
   }
 
-  LOG_INFO();
+  log::info("");
   return std::move(impl);
 }
 
diff --git a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
index d9398da..2842900 100644
--- a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
@@ -134,7 +134,7 @@
     return false;
   }
 
-  LOG_INFO();
+  log::info("");
   le_audio_sink_hal_state_ = HAL_STOPPED;
   return this->InitAudioSinkThread();
 }
@@ -145,7 +145,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   worker_thread_->ShutDown();
 
   if (halSinkInterface_) {
@@ -365,7 +365,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
 
   halSinkInterface_->StopSession();
   le_audio_sink_hal_state_ = HAL_STOPPED;
@@ -392,7 +392,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   if (IS_FLAG_ENABLED(leaudio_start_stream_race_fix)) {
     halSinkInterface_->ConfirmStreamingRequestV2();
   } else {
@@ -418,7 +418,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSinkInterface_->SuspendedForReconfiguration();
 }
 
@@ -429,7 +429,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSinkInterface_->ReconfigurationComplete();
 }
 
@@ -440,7 +440,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   if (IS_FLAG_ENABLED(leaudio_start_stream_race_fix)) {
     halSinkInterface_->CancelStreamingRequestV2();
   } else {
@@ -455,7 +455,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSinkInterface_->SetRemoteDelay(remote_delay_ms);
 }
 
@@ -467,7 +467,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSinkInterface_->UpdateAudioConfigToHal(config);
 }
 
@@ -478,7 +478,7 @@
     return;
   }
 
-  LOG_INFO();
+  log::info("");
   halSinkInterface_->UpdateBroadcastAudioConfigToHal(config);
 }
 }  // namespace
@@ -492,7 +492,7 @@
     return nullptr;
   }
 
-  LOG_INFO();
+  log::info("");
   return std::move(impl);
 }
 
@@ -505,7 +505,7 @@
     return nullptr;
   }
 
-  LOG_INFO();
+  log::info("");
   return std::move(impl);
 }
 
diff --git a/system/bta/le_audio/broadcaster/broadcaster.cc b/system/bta/le_audio/broadcaster/broadcaster.cc
index 593095f..60e105f 100644
--- a/system/bta/le_audio/broadcaster/broadcaster.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster.cc
@@ -98,7 +98,7 @@
         current_phy_(PHY_LE_2M),
         audio_data_path_state_(AudioDataPathState::INACTIVE),
         le_audio_source_hal_client_(nullptr) {
-    LOG_INFO();
+    log::info("");
 
     /* Register State machine callbacks */
     BroadcastStateMachine::Initialize(&state_machine_callbacks_,
@@ -430,8 +430,7 @@
   /* Choose the dominating audio context when multiple contexts are mixed */
   LeAudioContextType ChooseConfigurationContextType(
       AudioContexts audio_contexts) {
-    LOG_DEBUG("Got contexts=%s",
-              bluetooth::common::ToString(audio_contexts).c_str());
+    log::debug("Got contexts={}", bluetooth::common::ToString(audio_contexts));
 
     /* Prioritize the most common use cases. */
     if (audio_contexts.any()) {
@@ -443,16 +442,15 @@
       };
       for (auto ct : context_priority_list) {
         if (audio_contexts.test(ct)) {
-          LOG_DEBUG("Selecting configuration context type: %s",
-                    ToString(ct).c_str());
+          log::debug("Selecting configuration context type: {}", ToString(ct));
           return ct;
         }
       }
     }
 
     auto fallback_config = LeAudioContextType::MEDIA;
-    LOG_DEBUG("Selecting configuration context type: %s",
-              ToString(fallback_config).c_str());
+    log::debug("Selecting configuration context type: {}",
+               ToString(fallback_config));
     return fallback_config;
   }
 
@@ -595,7 +593,7 @@
 
     if (public_features & bluetooth::le_audio::kLeAudioQualityHigh &&
         config->GetSamplingFrequencyHzMax() < 48000) {
-      LOG_WARN(
+      log::warn(
           "Preferred quality isn't supported. Fallback to standard audio "
           "quality");
       public_features &= (0xFFFF & ~bluetooth::le_audio::kLeAudioQualityHigh);
@@ -1158,7 +1156,7 @@
 
       if (!broadcast_config_.has_value() ||
           (broadcast_config_->subgroups.size() == 0)) {
-        LOG_ERROR("Codec was not configured properly");
+        log::error("Codec was not configured properly");
         return;
       }
 
@@ -1195,14 +1193,14 @@
     }
 
     virtual void OnAudioSuspend(void) override {
-      LOG_INFO();
+      log::info("");
       /* TODO: Should we suspend all broadcasts - remove BIGs? */
       if (instance)
         instance->audio_data_path_state_ = AudioDataPathState::SUSPENDED;
     }
 
     virtual void OnAudioResume(void) override {
-      LOG_INFO();
+      log::info("");
       if (!instance) return;
 
       /* TODO: Should we resume all broadcasts - recreate BIGs? */
@@ -1268,7 +1266,7 @@
     bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks,
     base::Callback<bool()> audio_hal_verifier) {
   std::scoped_lock<std::mutex> lock(instance_mutex);
-  LOG_INFO();
+  log::info("");
   if (instance) {
     log::error("Already initialized");
     return;
@@ -1299,13 +1297,13 @@
 bool LeAudioBroadcaster::IsLeAudioBroadcasterRunning() { return instance; }
 
 LeAudioBroadcaster* LeAudioBroadcaster::Get(void) {
-  LOG_INFO();
+  log::info("");
   CHECK(instance);
   return instance;
 }
 
 void LeAudioBroadcaster::Stop(void) {
-  LOG_INFO();
+  log::info("");
 
   if (instance) {
     instance->Stop();
@@ -1314,7 +1312,7 @@
 
 void LeAudioBroadcaster::Cleanup(void) {
   std::scoped_lock<std::mutex> lock(instance_mutex);
-  LOG_INFO();
+  log::info("");
 
   if (instance == nullptr) return;
 
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index ace5303..5d4c6b2 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -5409,7 +5409,8 @@
   }
 
   void HandlePendingDeviceDisconnection(LeAudioDeviceGroup* group) {
-    LOG_DEBUG();
+    log::debug("");
+
     auto leAudioDevice = group->GetFirstDevice();
     while (leAudioDevice) {
       if (leAudioDevice->closing_stream_for_disconnection_) {
@@ -5817,8 +5818,7 @@
   void ClientAudioInterfaceRelease() {
     auto group = aseGroups_.FindById(active_group_id_);
     if (!group) {
-      LOG(ERROR) << __func__
-                 << ", Invalid group: " << static_cast<int>(active_group_id_);
+      log::error("Invalid group: {}", static_cast<int>(active_group_id_));
     } else {
       handleAsymmetricPhyForUnicast(group);
       log::info("ClientAudioInterfaceRelease - cleanup");
@@ -5862,13 +5862,13 @@
     }
 
     if (group->dsa_.mode != DsaMode::ISO_SW) {
-      LOG_WARN("ISO packets received over HCI in DSA mode: %d",
-               group->dsa_.mode);
+      log::warn("ISO packets received over HCI in DSA mode: {}",
+                group->dsa_.mode);
       return false;
     }
 
     if (iso_data_callback == nullptr) {
-      LOG_WARN("Dsa data consumer not registered");
+      log::warn("Dsa data consumer not registered");
       return false;
     }
 
@@ -6289,7 +6289,7 @@
     return false;
   }
 
-  LOG_INFO("ISO data consumer changed");
+  log::info("ISO data consumer changed");
   iso_data_callback = callback;
   return true;
 }
diff --git a/system/bta/le_audio/client_parser.cc b/system/bta/le_audio/client_parser.cc
index 857f760..ea1ac99 100644
--- a/system/bta/le_audio/client_parser.cc
+++ b/system/bta/le_audio/client_parser.cc
@@ -320,7 +320,7 @@
             }
             return cur_len + conf.codec_config.size();
           }
-          LOG_ERROR("Error parsing codec configuration LTV data.");
+          log::error("Error parsing codec configuration LTV data.");
         }
 
         conf_ents_str << "\t"
diff --git a/system/bta/le_audio/codec_manager.cc b/system/bta/le_audio/codec_manager.cc
index 76353e7..145ea9f 100644
--- a/system/bta/le_audio/codec_manager.cc
+++ b/system/bta/le_audio/codec_manager.cc
@@ -206,7 +206,8 @@
   const AudioSetConfigurations* GetSupportedCodecConfigurations(
       types::LeAudioContextType ctx_type) {
     if (GetCodecLocation() == le_audio::types::CodecLocation::ADSP) {
-      LOG_VERBOSE("Get offload config for the context type: %d", (int)ctx_type);
+      log::verbose("Get offload config for the context type: {}",
+                   (int)ctx_type);
 
       // TODO: Need to have a mechanism to switch to software session if offload
       // doesn't support.
@@ -215,7 +216,7 @@
                  : nullptr;
     }
 
-    LOG_VERBOSE("Get software config for the context type: %d", (int)ctx_type);
+    log::verbose("Get software config for the context type: {}", (int)ctx_type);
     return AudioSetConfigurationProvider::Get()->GetConfigurations(ctx_type);
   }
 
@@ -342,7 +343,7 @@
     }
 
     if (broadcast_target_config == -1) {
-      LOG_ERROR(
+      log::error(
           "There is no valid broadcast offload config with preferred_quality");
       return nullptr;
     }
@@ -388,8 +389,8 @@
 
     auto offload_config = GetBroadcastOffloadConfig(BIG_audio_quality);
     if (offload_config == nullptr) {
-      LOG_ERROR("No Offload configuration supported for quality index: %d.",
-                BIG_audio_quality);
+      log::error("No Offload configuration supported for quality index: {}.",
+                 BIG_audio_quality);
       return nullptr;
     }
 
@@ -438,7 +439,7 @@
     }
 
     if (subgroup_quality.size() > 1) {
-      LOG_ERROR("More than one subgroup is not supported!");
+      log::error("More than one subgroup is not supported!");
     }
 
     return std::make_unique<broadcaster::BroadcastConfiguration>(
@@ -465,7 +466,7 @@
 
     if (broadcast_target_config == -1 ||
         broadcast_target_config >= (int)supported_broadcast_config.size()) {
-      LOG_ERROR("There is no valid broadcast offload config");
+      log::error("There is no valid broadcast offload config");
       return;
     }
 
@@ -685,8 +686,8 @@
 
         // Check for number of ASEs mismatch
         if (adsp_set_ase_confs.size() != software_set_ase_confs.size()) {
-          LOG_ERROR(
-              "%s: ADSP config size mismatches the software: %zu != %zu",
+          log::error(
+              "{}: ADSP config size mismatches the software: {} != {}",
               direction == types::kLeAudioDirectionSink ? "Sink" : "Source",
               adsp_set_ase_confs.size(), software_set_ase_confs.size());
           continue;
diff --git a/system/bta/le_audio/device_groups.cc b/system/bta/le_audio/device_groups.cc
index 2dd37b1..64f29e4 100644
--- a/system/bta/le_audio/device_groups.cc
+++ b/system/bta/le_audio/device_groups.cc
@@ -486,7 +486,7 @@
 
 uint8_t LeAudioDeviceGroup::GetPacking(void) const {
   if (!stream_conf.conf) {
-    LOG_ERROR("No stream configuration has been set.");
+    log::error("No stream configuration has been set.");
     return bluetooth::hci::kIsoCigPackingSequential;
   }
   return stream_conf.conf->packing;
@@ -943,19 +943,21 @@
     /* Choose the group configuration strategy based on PAC records */
     strategy_ = GetGroupSinkStrategyFromPacs(expected_group_size);
 
-    LOG_INFO("Group strategy set to: %s", [](types::LeAudioConfigurationStrategy
-                                                 strategy) {
-      switch (strategy) {
-        case types::LeAudioConfigurationStrategy::MONO_ONE_CIS_PER_DEVICE:
-          return "MONO_ONE_CIS_PER_DEVICE";
-        case types::LeAudioConfigurationStrategy::STEREO_TWO_CISES_PER_DEVICE:
-          return "STEREO_TWO_CISES_PER_DEVICE";
-        case types::LeAudioConfigurationStrategy::STEREO_ONE_CIS_PER_DEVICE:
-          return "STEREO_ONE_CIS_PER_DEVICE";
-        default:
-          return "RFU";
-      }
-    }(*strategy_));
+    log::info(
+        "Group strategy set to: {}",
+        [](types::LeAudioConfigurationStrategy strategy) {
+          switch (strategy) {
+            case types::LeAudioConfigurationStrategy::MONO_ONE_CIS_PER_DEVICE:
+              return "MONO_ONE_CIS_PER_DEVICE";
+            case types::LeAudioConfigurationStrategy::
+                STEREO_TWO_CISES_PER_DEVICE:
+              return "STEREO_TWO_CISES_PER_DEVICE";
+            case types::LeAudioConfigurationStrategy::STEREO_ONE_CIS_PER_DEVICE:
+              return "STEREO_ONE_CIS_PER_DEVICE";
+            default:
+              return "RFU";
+          }
+        }(*strategy_));
   }
   return *strategy_;
 }
@@ -1309,9 +1311,8 @@
    */
   for (auto direction :
        {types::kLeAudioDirectionSink, types::kLeAudioDirectionSource}) {
-    LOG_DEBUG("Looking for configuration: %s - %s",
-              audio_set_conf->name.c_str(),
-              (direction == types::kLeAudioDirectionSink ? "Sink" : "Source"));
+    log::debug("Looking for configuration: {} - {}", audio_set_conf->name,
+               (direction == types::kLeAudioDirectionSink ? "Sink" : "Source"));
     auto const& ase_confs = audio_set_conf->confs.get(direction);
 
     ASSERT_LOG(
@@ -1324,11 +1325,11 @@
     auto const ase_cnt = ase_confs.size();
 
     if (ase_cnt == 0) {
-      LOG_ERROR("ASE count is 0");
+      log::error("ASE count is 0");
       continue;
     }
     if (device_cnt == 0) {
-      LOG_ERROR("Device count is 0");
+      log::error("Device count is 0");
       continue;
     }
 
@@ -1356,7 +1357,7 @@
          device != nullptr && required_device_cnt > 0;
          device = GetNextDevice(device)) {
       if (device->ases_.empty()) {
-        LOG_ERROR("Device has no ASEs.");
+        log::error("Device has no ASEs.");
         continue;
       }
 
@@ -1366,12 +1367,12 @@
 
       for (auto const& ent : ase_confs) {
         if (!device->GetCodecConfigurationSupportedPac(direction, ent.codec)) {
-          LOG_DEBUG("Insufficient PAC");
+          log::debug("Insufficient PAC");
           continue;
         }
 
         if (!CheckIfStrategySupported(strategy, ent, direction, *device)) {
-          LOG_DEBUG("Strategy not supported");
+          log::debug("Strategy not supported");
           continue;
         }
         for (auto& ase : device->ases_) {
@@ -1385,8 +1386,8 @@
       }
 
       if (needed_ase_per_dev > 0) {
-        LOG_DEBUG("Not enough ASEs on the device (needs %d more).",
-                  needed_ase_per_dev);
+        log::debug("Not enough ASEs on the device (needs {} more).",
+                   needed_ase_per_dev);
         return false;
       }
 
@@ -1395,8 +1396,8 @@
 
     if (required_device_cnt > 0) {
       /* Don't left any active devices if requirements are not met */
-      LOG_DEBUG(
-          "Could not configure all the devices for direction: %s",
+      log::debug(
+          "Could not configure all the devices for direction: {}",
           (direction == types::kLeAudioDirectionSink ? "Sink" : "Source"));
       return false;
     }
@@ -1463,11 +1464,11 @@
        {types::kLeAudioDirectionSink, types::kLeAudioDirectionSource}) {
     auto direction_str =
         (direction == types::kLeAudioDirectionSink ? "Sink" : "Source");
-    LOG_DEBUG("%s: Looking for requirements: %s", direction_str,
-              audio_set_conf->name.c_str());
+    log::debug("{}: Looking for requirements: {}", direction_str,
+               audio_set_conf->name);
 
     if (audio_set_conf->confs.get(direction).empty()) {
-      LOG_WARN("No %s configuration available.", direction_str);
+      log::warn("No {} configuration available.", direction_str);
       continue;
     }
 
@@ -1870,20 +1871,20 @@
     const auto& confs = audio_set_conf->confs.get(direction);
     if (confs.size() == 0) continue;
 
-    LOG_INFO("Looking for requirements: %s - %s", audio_set_conf->name.c_str(),
-             (direction == 1 ? "snk" : "src"));
+    log::info("Looking for requirements: {} - {}", audio_set_conf->name,
+              (direction == 1 ? "snk" : "src"));
     for (const auto& ent : confs) {
       if (!leAudioDevice->GetCodecConfigurationSupportedPac(direction,
                                                             ent.codec)) {
-        LOG_INFO("Configuration is NOT supported by device %s",
-                 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
+        log::info("Configuration is NOT supported by device {}",
+                  ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
         return false;
       }
     }
   }
 
-  LOG_INFO("Configuration is supported by device %s",
-           ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
+  log::info("Configuration is supported by device {}",
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
   return true;
 }
 
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index c47e5f9..40b22b9 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -721,8 +721,8 @@
 
     for (const auto pac : pac_recs) {
       if (!utils::IsCodecUsingLtvFormat(pac.codec_id)) {
-        LOG_WARN("Unknown codec PAC record for codec: %s",
-                 bluetooth::common::ToString(pac.codec_id).c_str());
+        log::warn("Unknown codec PAC record for codec: {}",
+                  bluetooth::common::ToString(pac.codec_id));
         continue;
       }
       ASSERT_LOG(!pac.codec_spec_caps.IsEmpty(),
@@ -833,10 +833,11 @@
   // Take the preferences if possible
   if (preferred_phy && (phy_bitmask & preferred_phy)) {
     phy_bitmask &= preferred_phy;
-    LOG_DEBUG("Using ASE preferred phy 0x%02x", static_cast<int>(phy_bitmask));
+    log::debug("Using ASE preferred phy 0x{:02x}",
+               static_cast<int>(phy_bitmask));
   } else {
-    LOG_WARN(
-        "ASE preferred 0x%02x has nothing common with phy_bitfield  0x%02x ",
+    log::warn(
+        "ASE preferred 0x{:02x} has nothing common with phy_bitfield  0x{:02x}",
         static_cast<int>(preferred_phy), static_cast<int>(phy_bitmask));
   }
   return phy_bitmask;
diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc
index 2930e48..74352ed 100644
--- a/system/bta/le_audio/devices_test.cc
+++ b/system/bta/le_audio/devices_test.cc
@@ -755,7 +755,7 @@
             continue;
           }
           if (device_cnt == 0) {
-            LOG_ERROR("Device count is 0");
+            log::error("Device count is 0");
             continue;
           }
 
@@ -764,18 +764,18 @@
            * to active
            */
           if (device_cnt != data_size) {
-            LOG_DEBUG("Device count mismatch device!=data (%d!=%d)",
-                      static_cast<int>(device_cnt),
-                      static_cast<int>(data_size));
+            log::debug("Device count mismatch device!=data ({}!={})",
+                       static_cast<int>(device_cnt),
+                       static_cast<int>(data_size));
             interesting_configuration = false;
           }
 
           /* Make sure the strategy is the expected one */
           if (direction == kLeAudioDirectionSink &&
               group_->GetGroupSinkStrategy() != strategy) {
-            LOG_DEBUG("Sink strategy mismatch group!=cfg.entry (%d!=%d)",
-                      static_cast<int>(group_->GetGroupSinkStrategy()),
-                      static_cast<int>(strategy));
+            log::debug("Sink strategy mismatch group!=cfg.entry ({}!={})",
+                       static_cast<int>(group_->GetGroupSinkStrategy()),
+                       static_cast<int>(strategy));
             interesting_configuration = false;
           }
 
diff --git a/system/bta/le_audio/le_audio_health_status_test.cc b/system/bta/le_audio/le_audio_health_status_test.cc
index 19a7492..0a6ecc0 100644
--- a/system/bta/le_audio/le_audio_health_status_test.cc
+++ b/system/bta/le_audio/le_audio_health_status_test.cc
@@ -38,11 +38,6 @@
 using bluetooth::le_audio::LeAudioHealthGroupStatType;
 using bluetooth::le_audio::LeAudioHealthStatus;
 
-static const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
 LeAudioHealthBasedAction recommendation_in_callback =
     LeAudioHealthBasedAction::NONE;
 RawAddress address_in_callback = RawAddress::kEmpty;
@@ -60,7 +55,6 @@
   void SetUp() override {
     reset_mock_function_count_map();
     group_ = new LeAudioDeviceGroup(group_id_);
-    bluetooth::common::InitFlags::Load(test_flags);
     le_audio_health_status_instance_ = LeAudioHealthStatus::Get();
     le_audio_health_status_instance_->RegisterCallback(
         base::BindRepeating(healthCallback));
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 66fdf2a..ff2a7d4 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -240,8 +240,8 @@
                                       codec_config_setting.params);
   }
 
-  LOG_ERROR("Codec %s, seems to be not supported here.",
-            bluetooth::common::ToString(codec_id).c_str());
+  log::error("Codec {}, seems to be not supported here.",
+             bluetooth::common::ToString(codec_id));
   return false;
 }
 
@@ -535,7 +535,7 @@
   LeAudioLtvMap ltv_map;
   success = ltv_map.Parse(p_value, len);
   if (!success) {
-    LOG(ERROR) << __func__ << "Error parsing LTV map";
+    log::error("Error parsing LTV map");
   }
   return ltv_map;
 }
diff --git a/system/bta/le_audio/le_audio_utils.cc b/system/bta/le_audio/le_audio_utils.cc
index 1e4ed1e..3b328bc 100644
--- a/system/bta/le_audio/le_audio_utils.cc
+++ b/system/bta/le_audio/le_audio_utils.cc
@@ -343,7 +343,7 @@
     const struct types::acs_ac_record& record,
     std::vector<bluetooth::le_audio::btle_audio_codec_config_t>& vec) {
   if (!utils::IsCodecUsingLtvFormat(record.codec_id)) {
-    LOG_WARN(
+    log::warn(
         "Unknown codec capability format. Unable to report known codec "
         "parameters.");
     return;
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index f302170..6fa4136 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -1972,10 +1972,10 @@
         out_cfg.max_transport_latency = rsp.max_transport_latency;
         out_cfg.retrans_nb = rsp.preferred_retrans_nb;
         out_cfg.phy = leAudioDevice->GetPreferredPhyBitmask(rsp.preferred_phy);
-        LOG_INFO(
-            "Using server preferred QoS settings. Max Transport Latency: %d, "
-            "Retransmission Number: %d, Phy: %d",
-            +out_cfg.max_transport_latency, out_cfg.retrans_nb, +out_cfg.phy);
+        log::info(
+            "Using server preferred QoS settings. Max Transport Latency: {}, "
+            "Retransmission Number: {}, Phy: {}",
+            out_cfg.max_transport_latency, out_cfg.retrans_nb, out_cfg.phy);
       }
     };
 
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index b39f1fd..3c73042 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -60,10 +60,6 @@
 extern struct fake_osi_alarm_set_on_mloop fake_osi_alarm_set_on_mloop_;
 
 void osi_property_set_bool(const char* key, bool value);
-static const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
 
 constexpr uint8_t media_ccid = 0xC0;
 constexpr auto media_context = LeAudioContextType::MEDIA;
@@ -266,7 +262,6 @@
   bool stop_inject_configured_ase_after_first_ase_configured_;
 
   virtual void SetUp() override {
-    bluetooth::common::InitFlags::Load(test_flags);
     reset_mock_function_count_map();
     bluetooth::manager::SetMockBtmInterface(&btm_interface);
     gatt::SetMockBtaGattInterface(&gatt_interface);
@@ -4404,7 +4399,7 @@
   fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
   ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop"));
 
-  LOG_INFO("OnStateTransitionTimeout");
+  log::info("OnStateTransitionTimeout");
 
   /* Simulate On State timeout */
   group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
@@ -4458,7 +4453,7 @@
   fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
   ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop"));
 
-  LOG_INFO("OnStateTransitionTimeout");
+  log::info("OnStateTransitionTimeout");
 
   /* Simulate On State timeout */
   group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
@@ -4512,7 +4507,7 @@
   fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
   ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop"));
 
-  LOG_INFO("OnStateTransitionTimeout");
+  log::info("OnStateTransitionTimeout");
 
   /* Simulate On State timeout */
   group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
diff --git a/system/bta/le_audio/storage_helper_test.cc b/system/bta/le_audio/storage_helper_test.cc
index b068a6a..b7084b2 100644
--- a/system/bta/le_audio/storage_helper_test.cc
+++ b/system/bta/le_audio/storage_helper_test.cc
@@ -22,11 +22,6 @@
 
 using bluetooth::le_audio::LeAudioDevice;
 
-const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
 namespace bluetooth::le_audio {
 RawAddress GetTestAddress(uint8_t index) {
   CHECK_LT(index, UINT8_MAX);
@@ -34,12 +29,7 @@
   return result;
 }
 
-class StorageHelperTest : public ::testing::Test {
- protected:
-  void SetUp() override { bluetooth::common::InitFlags::Load(test_flags); }
-
-  void TearDown() override {}
-};
+class StorageHelperTest : public ::testing::Test {};
 
 TEST(StorageHelperTest, DeserializeSinkPacs) {
   // clang-format off
diff --git a/system/bta/ras/ras_server.cc b/system/bta/ras/ras_server.cc
index a30e956..6d93b1a 100644
--- a/system/bta/ras/ras_server.cc
+++ b/system/bta/ras/ras_server.cc
@@ -30,8 +30,6 @@
 using namespace ::ras;
 using namespace ::ras::uuid;
 
-using bluetooth::ras::ProcedureDoneStatus;
-
 namespace {
 
 class RasServerImpl;
@@ -76,11 +74,10 @@
   }
 
   void PushProcedureData(RawAddress address, uint16_t procedure_counter,
-                         ProcedureDoneStatus procedure_done_status,
-                         std::vector<uint8_t> data) {
-    log::info("{}, counter:{}, procedure_done_status:{}, with size {}",
-              ADDRESS_TO_LOGGABLE_STR(address), procedure_counter,
-              (uint16_t)procedure_done_status, data.size());
+                         bool is_last, std::vector<uint8_t> data) {
+    log::info("{}, counter:{}, is_last:{}, with size {}",
+              ADDRESS_TO_LOGGABLE_STR(address), procedure_counter, is_last,
+              data.size());
     tBLE_BD_ADDR ble_bd_addr;
     ResolveAddress(ble_bd_addr, address);
 
@@ -96,8 +93,7 @@
     data_buffer.segments_.push_back(data);
 
     // Send data ready
-    if (procedure_done_status == ProcedureDoneStatus::ALL_RESULTS_COMPLETE ||
-        procedure_done_status == ProcedureDoneStatus::ABORTED) {
+    if (is_last) {
       uint16_t ccc_value =
           tracker.ccc_values_[kRasRangingDataReadyCharacteristic];
       if (ccc_value == GATT_CLT_CONFIG_NONE) {
diff --git a/system/bta/test/bta_api_test.cc b/system/bta/test/bta_api_test.cc
index 37f2e88..65c91d1 100644
--- a/system/bta/test/bta_api_test.cc
+++ b/system/bta/test/bta_api_test.cc
@@ -29,21 +29,9 @@
 
 using namespace std::chrono_literals;
 
-namespace {
-
-const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
-}  // namespace
-
 class BtaApiTest : public testing::Test {
  protected:
-  void SetUp() override {
-    reset_mock_function_count_map();
-    bluetooth::common::InitFlags::Load(test_flags);
-  }
+  void SetUp() override { reset_mock_function_count_map(); }
   void TearDown() override {}
 };
 
diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc
index ddbaabc..40dc9a1 100644
--- a/system/bta/vc/vc.cc
+++ b/system/bta/vc/vc.cc
@@ -930,12 +930,12 @@
   }
 
   void Mute(std::variant<RawAddress, int> addr_or_group_id) override {
-    LOG_DEBUG();
+    log::debug("");
     MuteUnmute(addr_or_group_id, true /* mute */);
   }
 
   void UnMute(std::variant<RawAddress, int> addr_or_group_id) override {
-    LOG_DEBUG();
+    log::debug("");
     MuteUnmute(addr_or_group_id, false /* mute */);
   }
 
diff --git a/system/btif/src/btif_hh.cc b/system/btif/src/btif_hh.cc
index 457d818..08d5c64 100644
--- a/system/btif/src/btif_hh.cc
+++ b/system/btif/src/btif_hh.cc
@@ -493,15 +493,26 @@
              conn.link_spec.ToRedactedStringForLogging(), conn.status,
              conn.handle);
 
-  if (IS_FLAG_ENABLED(allow_switching_hid_and_hogp) &&
-      conn.link_spec.transport != BT_TRANSPORT_AUTO) {
+  if (IS_FLAG_ENABLED(allow_switching_hid_and_hogp)) {
+    // Initialize with disconnected/accepting state based on reconnection policy
+    bthh_connection_state_t dev_status =
+        hh_get_state_on_disconnect(conn.link_spec);
+
+    // Use current state if the device instance already exists
     btif_hh_device_t* p_dev = btif_hh_find_dev_by_link_spec(conn.link_spec);
-    if ((p_dev != NULL) && (p_dev->dev_status != BTHH_CONN_STATE_ACCEPTING &&
-                            p_dev->dev_status != BTHH_CONN_STATE_CONNECTING)) {
+    if (p_dev != nullptr) {
+      dev_status = p_dev->dev_status;
+    }
+
+    if (dev_status != BTHH_CONN_STATE_ACCEPTING &&
+        dev_status != BTHH_CONN_STATE_CONNECTING) {
       log::warn("Reject Incoming HID Connection, device: {}, state: {}",
-                p_dev->link_spec.ToRedactedStringForLogging(),
-                p_dev->dev_status);
-      p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;
+                conn.link_spec.ToRedactedStringForLogging(), dev_status);
+
+      if (p_dev != nullptr) {
+        p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;
+      }
+
       hh_connect_complete(conn.handle, conn.link_spec,
                           BTIF_HH_DEV_DISCONNECTED);
       return;
@@ -618,6 +629,9 @@
   }
 
   if (hh_add_device(link_spec, attr_mask, reconnect_allowed)) {
+    if (IS_FLAG_ENABLED(allow_switching_hid_and_hogp) && reconnect_allowed) {
+      BTHH_STATE_UPDATE(link_spec, BTHH_CONN_STATE_ACCEPTING);
+    }
     BTA_HhAddDev(link_spec, attr_mask, sub_class, app_id, dscp_info);
   }
 }
@@ -1619,7 +1633,8 @@
     btif_hh_added_device_t* added_dev = btif_hh_find_added_dev(link_spec);
     if (added_dev != nullptr) {
       added_dev->reconnect_allowed = reconnect_allowed;
-      btif_storage_set_hid_connection_policy(link_spec, reconnect_allowed);
+      btif_storage_set_hid_connection_policy(added_dev->link_spec,
+                                             reconnect_allowed);
     }
   }
 
diff --git a/system/btif/src/btif_rc.cc b/system/btif/src/btif_rc.cc
index 79d4a69..26b1d2e 100644
--- a/system/btif/src/btif_rc.cc
+++ b/system/btif/src/btif_rc.cc
@@ -860,10 +860,9 @@
 
   // check if already some RC is connected
   if (p_dev->rc_connected) {
-    LOG_ERROR(
-        "%s: Got RC OPEN in connected state, Connected RC: %d \
-            and Current RC: %d",
-        __func__, p_dev->rc_handle, p_rc_open->rc_handle);
+    log::error(
+        "Got RC OPEN in connected state, Connected RC: {} and Current RC: {}",
+        p_dev->rc_handle, p_rc_open->rc_handle);
     if (p_dev->rc_handle != p_rc_open->rc_handle &&
         p_dev->rc_addr != p_rc_open->peer_addr) {
       log::verbose("Got RC connected for some other handle");
@@ -1536,10 +1535,10 @@
     }
 
     if (!bSent) {
-      LOG_VERBOSE(
-          "%s: Notification not sent, as there are no RC connections or the \
-                CT has not subscribed for event_id: %s",
-          __func__, dump_rc_notification_event_id(event_id));
+      log::verbose(
+          "Notification not sent, as there are no RC connections or the CT has "
+          "not subscribed for event_id: {}",
+          dump_rc_notification_event_id(event_id));
     }
   } else {
     /* All other commands go here */
@@ -1727,10 +1726,10 @@
       uint8_t num_attr;
       num_attr = pavrc_cmd->get_items.attr_count;
 
-      LOG_VERBOSE(
-          "%s: AVRC_PDU_GET_FOLDER_ITEMS num_attr: %d, start_item [%d] \
-                end_item [%d]",
-          __func__, num_attr, pavrc_cmd->get_items.start_item,
+      log::verbose(
+          "AVRC_PDU_GET_FOLDER_ITEMS num_attr: {}, start_item [{}] end_item "
+          "[{}]",
+          num_attr, pavrc_cmd->get_items.start_item,
           pavrc_cmd->get_items.end_item);
 
       /* num_attr requested:
diff --git a/system/device/test/device_iot_config_test.cc b/system/device/test/device_iot_config_test.cc
index c440401..cbdfff1 100644
--- a/system/device/test/device_iot_config_test.cc
+++ b/system/device/test/device_iot_config_test.cc
@@ -36,13 +36,11 @@
 using namespace testing;
 
 const char* test_flags_feature_enabled[] = {
-    "INIT_logging_debug_enabled_for_all=true",
     "INIT_device_iot_config_logging=true",
     nullptr,
 };
 
 const char* test_flags_feature_disabled[] = {
-    "INIT_logging_debug_enabled_for_all=true",
     "INIT_device_iot_config_logging=false",
     nullptr,
 };
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index dd4136e..695a3e5 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -552,10 +552,12 @@
         cfi: true,
     },
     static_libs: [
+        "libbase",
         "libbluetooth_log",
         "libbluetooth_rust_interop",
         "libbt_shim_bridge",
         "libbt_shim_ffi",
+        "liblog",
     ],
     target: {
         android: {
diff --git a/system/gd/fuzz/helpers.h b/system/gd/fuzz/helpers.h
index c9cbd18..77e9a38 100644
--- a/system/gd/fuzz/helpers.h
+++ b/system/gd/fuzz/helpers.h
@@ -21,7 +21,7 @@
 #include <cstdint>
 #include <vector>
 
-#include "os/handler.h"
+#include "common/contextual_callback.h"
 
 namespace bluetooth {
 namespace fuzz {
@@ -43,7 +43,9 @@
   if (!packet.IsValid()) {
     return;
   }
-  callback.InvokeIfNotEmpty(packet);
+  if (!callback.IsEmpty()) {
+    callback.Invoke(packet);
+  }
 }
 
 template <typename TView>
@@ -52,7 +54,9 @@
   if (!packet.IsValid()) {
     return;
   }
-  callback.InvokeIfNotEmpty(packet);
+  if (!callback.IsEmpty()) {
+    callback.Invoke(packet);
+  }
 }
 
 }  // namespace fuzz
diff --git a/system/gd/hal/snoop_logger_socket_test.cc b/system/gd/hal/snoop_logger_socket_test.cc
index da379ab..6659585 100644
--- a/system/gd/hal/snoop_logger_socket_test.cc
+++ b/system/gd/hal/snoop_logger_socket_test.cc
@@ -28,11 +28,6 @@
 #include "hal/syscall_wrapper_impl.h"
 #include "hal/syscall_wrapper_mock.h"
 
-static const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
 namespace testing {
 
 using bluetooth::hal::SnoopLoggerCommon;
@@ -44,9 +39,6 @@
 
 class SnoopLoggerSocketModuleTest : public Test {
  protected:
-  void SetUp() override {
-    bluetooth::common::InitFlags::Load(test_flags);
-  }
   SnoopLoggerSocketModuleTest() : sls(&mock) {}
 
   void InitializeCommunicationsSuccess(SnoopLoggerSocket& sls, SyscallWrapperMock& mock) {
diff --git a/system/gd/hal/snoop_logger_socket_thread_test.cc b/system/gd/hal/snoop_logger_socket_thread_test.cc
index e8d1c68..37be6a8 100644
--- a/system/gd/hal/snoop_logger_socket_thread_test.cc
+++ b/system/gd/hal/snoop_logger_socket_thread_test.cc
@@ -28,11 +28,6 @@
 #include "hal/syscall_wrapper_impl.h"
 #include "os/utils.h"
 
-static const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
 namespace testing {
 
 using bluetooth::hal::SnoopLoggerCommon;
@@ -42,14 +37,7 @@
 
 static constexpr int INVALID_FD = -1;
 
-class SnoopLoggerSocketThreadModuleTest : public Test {
- protected:
-  void SetUp() override {
-    bluetooth::common::InitFlags::Load(test_flags);
-  }
-
-  void TearDown() override {}
-};
+class SnoopLoggerSocketThreadModuleTest : public Test {};
 
 TEST_F(SnoopLoggerSocketThreadModuleTest, socket_start_no_stop_test) {
   {
diff --git a/system/gd/hal/snoop_logger_test.cc b/system/gd/hal/snoop_logger_test.cc
index ba8f538..d4ca3a2 100644
--- a/system/gd/hal/snoop_logger_test.cc
+++ b/system/gd/hal/snoop_logger_test.cc
@@ -115,11 +115,6 @@
 using bluetooth::hal::SnoopLogger;
 using namespace std::chrono_literals;
 
-const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
 // Expose protected constructor for test
 class TestSnoopLoggerModule : public SnoopLogger {
  public:
@@ -195,8 +190,6 @@
     ASSERT_FALSE(std::filesystem::exists(temp_snooz_log_last_));
 
     test_registry = new TestModuleRegistry();
-
-    bluetooth::common::InitFlags::Load(test_flags);
   }
 
   void TearDown() override {
diff --git a/system/gd/hci/acl_manager.cc b/system/gd/hci/acl_manager.cc
index 35e1232..853d4f7 100644
--- a/system/gd/hci/acl_manager.cc
+++ b/system/gd/hci/acl_manager.cc
@@ -414,6 +414,10 @@
   return pimpl_->le_impl_->HACK_get_handle(address);
 }
 
+Address AclManager::HACK_GetLeAddress(uint16_t connection_handle) {
+  return pimpl_->le_impl_->HACK_get_address(connection_handle);
+}
+
 void AclManager::HACK_SetAclTxPriority(uint8_t handle, bool high_priority) {
   CallOn(pimpl_->round_robin_scheduler_, &RoundRobinScheduler::SetLinkPriority, handle, high_priority);
 }
diff --git a/system/gd/hci/acl_manager.h b/system/gd/hci/acl_manager.h
index 6814f85..db945a0 100644
--- a/system/gd/hci/acl_manager.h
+++ b/system/gd/hci/acl_manager.h
@@ -171,6 +171,7 @@
  private:
   virtual uint16_t HACK_GetHandle(const Address address);
   virtual uint16_t HACK_GetLeHandle(const Address address);
+  virtual Address HACK_GetLeAddress(uint16_t connection_handle);
 
   virtual void HACK_SetAclTxPriority(uint8_t handle, bool high_priority);
 
diff --git a/system/gd/hci/acl_manager/acl_scheduler.cc b/system/gd/hci/acl_manager/acl_scheduler.cc
index cbf45f7..e19c4fe 100644
--- a/system/gd/hci/acl_manager/acl_scheduler.cc
+++ b/system/gd/hci/acl_manager/acl_scheduler.cc
@@ -61,7 +61,7 @@
       if (entry != nullptr && entry->address == address) {
         // If so, clear the current entry and advance the queue
         outgoing_entry_.reset();
-        handle_outgoing_connection.InvokeIfNotEmpty();
+        handle_outgoing_connection.Invoke();
         try_dequeue_next_operation();
         return;
       }
@@ -70,9 +70,9 @@
     // Otherwise check if it's an incoming request and advance the queue if so
     if (incoming_connecting_address_set_.find(address) != incoming_connecting_address_set_.end()) {
       incoming_connecting_address_set_.erase(address);
-      handle_incoming_connection.InvokeIfNotEmpty();
+      handle_incoming_connection.Invoke();
     } else {
-      handle_unknown_connection.InvokeIfNotEmpty(set_of_incoming_connecting_addresses());
+      handle_unknown_connection.Invoke(set_of_incoming_connecting_addresses());
     }
     try_dequeue_next_operation();
   }
diff --git a/system/gd/hci/acl_manager/classic_impl_test.cc b/system/gd/hci/acl_manager/classic_impl_test.cc
index f92521b..7dcef4b 100644
--- a/system/gd/hci/acl_manager/classic_impl_test.cc
+++ b/system/gd/hci/acl_manager/classic_impl_test.cc
@@ -129,14 +129,14 @@
       common::ContextualOnceCallback<void()> handle_incoming_connection,
       common::ContextualOnceCallback<void(std::string)> handle_unknown_connection) override {
     if (handle_outgoing_connection_) {
-      handle_outgoing_connection.InvokeIfNotEmpty();
+      handle_outgoing_connection.Invoke();
       return;
     }
 
     if (handle_incoming_connection_) {
-      handle_incoming_connection.InvokeIfNotEmpty();
+      handle_incoming_connection.Invoke();
     } else {
-      handle_unknown_connection.InvokeIfNotEmpty("set_of_incoming_connecting_addresses()");
+      handle_unknown_connection.Invoke("set_of_incoming_connecting_addresses()");
     }
   }
 };
diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h
index f3db96d..cbed7b8 100644
--- a/system/gd/hci/acl_manager/le_impl.h
+++ b/system/gd/hci/acl_manager/le_impl.h
@@ -646,6 +646,10 @@
     return connections.HACK_get_handle(address);
   }
 
+  Address HACK_get_address(uint16_t connection_handle) {
+    return connections.getAddressWithType(connection_handle).GetAddress();
+  }
+
   void OnAdvertisingSetTerminated(
       uint16_t conn_handle,
       uint8_t adv_set_id,
diff --git a/system/gd/hci/distance_measurement_manager.cc b/system/gd/hci/distance_measurement_manager.cc
index c7aea0e..86cab99 100644
--- a/system/gd/hci/distance_measurement_manager.cc
+++ b/system/gd/hci/distance_measurement_manager.cc
@@ -456,6 +456,7 @@
       // Create a cs tracker with role reflector
       // TODO: Check ROLE via CS config. (b/304295768)
       cs_trackers_[connection_handle].role = CsRole::REFLECTOR;
+      cs_trackers_[connection_handle].address = acl_manager_->HACK_GetLeAddress(connection_handle);
     } else {
       send_le_cs_security_enable(connection_handle);
     }
@@ -712,11 +713,11 @@
       append_vector(procedure_data->ras_subevents, subevent_raw);
       // erase buffer
       procedure_data->ras_subevent_data_.clear();
-      send_on_demand_data(procedure_data);
+      send_on_demand_data(cs_trackers_[connection_handle].address, procedure_data);
     }
   }
 
-  void send_on_demand_data(CsProcedureData* procedure_data) {
+  void send_on_demand_data(Address address, CsProcedureData* procedure_data) {
     if (procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS) {
       procedure_data->segmentation_header_.last_segment_ = 1;
     }
@@ -727,7 +728,11 @@
         procedure_data->ras_subevents);
     auto raw_data = builder_to_bytes(std::move(builder));
     log::debug("counter: {}, size:{}", procedure_data->counter, (uint16_t)raw_data.size());
-    // TODO, push data to RAS server
+    distance_measurement_callbacks_->OnRasFragmentReady(
+        address,
+        procedure_data->counter,
+        procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS,
+        raw_data);
 
     procedure_data->ras_subevents.clear();
     procedure_data->segmentation_header_.first_segment_ = 0;
diff --git a/system/gd/hci/distance_measurement_manager.h b/system/gd/hci/distance_measurement_manager.h
index 2c1d645..17e6cdb 100644
--- a/system/gd/hci/distance_measurement_manager.h
+++ b/system/gd/hci/distance_measurement_manager.h
@@ -64,6 +64,8 @@
       int altitude_angle,
       int error_altitude_angle,
       DistanceMeasurementMethod method) = 0;
+  virtual void OnRasFragmentReady(
+      Address address, uint16_t procedure_counter, bool is_last, std::vector<uint8_t> raw_data) = 0;
 };
 
 class DistanceMeasurementManager : public bluetooth::Module {
diff --git a/system/gd/hci/fuzz/fuzz_hci_layer.cc b/system/gd/hci/fuzz/fuzz_hci_layer.cc
index 38fd22a..d2a3dda 100644
--- a/system/gd/hci/fuzz/fuzz_hci_layer.cc
+++ b/system/gd/hci/fuzz/fuzz_hci_layer.cc
@@ -181,8 +181,11 @@
 }
 
 void FuzzHciLayer::injectAclDisconnect(FuzzedDataProvider& fdp) {
-  acl_on_disconnect_.InvokeIfNotEmpty(fdp.ConsumeIntegral<uint16_t>(),
-                                      static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+  if (!acl_on_disconnect_.IsEmpty()) {
+    acl_on_disconnect_.Invoke(
+        fdp.ConsumeIntegral<uint16_t>(),
+        static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+  }
 }
 
 void FuzzHciLayer::injectLeAclEvent(std::vector<uint8_t> data) {
@@ -190,8 +193,11 @@
 }
 
 void FuzzHciLayer::injectLeAclDisconnect(FuzzedDataProvider& fdp) {
-  le_acl_on_disconnect_.InvokeIfNotEmpty(fdp.ConsumeIntegral<uint16_t>(),
-                                         static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+  if (!le_acl_on_disconnect_.IsEmpty()) {
+    le_acl_on_disconnect_.Invoke(
+        fdp.ConsumeIntegral<uint16_t>(),
+        static_cast<hci::ErrorCode>(fdp.ConsumeIntegral<uint8_t>()));
+  }
 }
 
 void FuzzHciLayer::injectLeAdvertisingEvent(std::vector<uint8_t> data) {
diff --git a/system/gd/hci/hci_layer.cc b/system/gd/hci/hci_layer.cc
index 709a651..7ed84fd 100644
--- a/system/gd/hci/hci_layer.cc
+++ b/system/gd/hci/hci_layer.cc
@@ -65,7 +65,6 @@
 }
 
 static void abort_after_time_out(OpCode op_code) {
-  bluetooth::os::LogMetricHciTimeoutEvent(static_cast<uint32_t>(op_code));
   ASSERT_LOG(false, "Done waiting for debug information after HCI timeout (%s)", OpCodeText(op_code).c_str());
 }
 
@@ -272,7 +271,8 @@
   void on_hci_timeout(OpCode op_code) {
     common::StopWatch::DumpStopWatchLog();
     log::error("Timed out waiting for 0x{:02x} ({})", op_code, OpCodeText(op_code));
-    // TODO: LogMetricHciTimeoutEvent(static_cast<uint32_t>(op_code));
+
+    bluetooth::os::LogMetricHciTimeoutEvent(static_cast<uint32_t>(op_code));
 
     log::error("Flushing {} waiting commands", command_queue_.size());
     // Clear any waiting commands (there is an abort coming anyway)
diff --git a/system/gd/hci/le_periodic_sync_manager.h b/system/gd/hci/le_periodic_sync_manager.h
index 8ee7bf6..ec97386 100644
--- a/system/gd/hci/le_periodic_sync_manager.h
+++ b/system/gd/hci/le_periodic_sync_manager.h
@@ -38,7 +38,7 @@
 namespace bluetooth {
 namespace hci {
 
-constexpr std::chrono::duration kPeriodicSyncTimeout = std::chrono::seconds(30);
+constexpr std::chrono::duration kPeriodicSyncTimeout = std::chrono::seconds(5);
 constexpr int kMaxSyncTransactions = 16;
 
 enum PeriodicSyncState : int {
diff --git a/system/gd/hci/le_periodic_sync_manager_test.cc b/system/gd/hci/le_periodic_sync_manager_test.cc
index 4910950..ed2c019 100644
--- a/system/gd/hci/le_periodic_sync_manager_test.cc
+++ b/system/gd/hci/le_periodic_sync_manager_test.cc
@@ -133,11 +133,6 @@
   mutable std::mutex mutex_;
 };
 
-const char* test_flags[] = {
-  "INIT_logging_debug_enabled_for_all=true",
-  nullptr,
-};
-
 class PeriodicSyncManagerTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -146,7 +141,6 @@
     test_le_scanning_interface_ = new TestLeScanningInterface();
     periodic_sync_manager_ = new PeriodicSyncManager(&mock_callbacks_);
     periodic_sync_manager_->Init(test_le_scanning_interface_, handler_);
-    bluetooth::common::InitFlags::Load(test_flags);
   }
 
   void TearDown() override {
diff --git a/system/gd/os/linux_generic/queue.tpp b/system/gd/os/linux_generic/queue.tpp
deleted file mode 100644
index 24113b1..0000000
--- a/system/gd/os/linux_generic/queue.tpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-template <typename T>
-Queue<T>::Queue(size_t capacity) : enqueue_(capacity), dequeue_(0){};
-
-template <typename T>
-Queue<T>::~Queue() {
-  ASSERT_LOG(enqueue_.handler_ == nullptr, "Enqueue is not unregistered");
-  ASSERT_LOG(dequeue_.handler_ == nullptr, "Dequeue is not unregistered");
-};
-
-template <typename T>
-void Queue<T>::RegisterEnqueue(Handler* handler, EnqueueCallback callback) {
-  std::lock_guard<std::mutex> lock(mutex_);
-  ASSERT(enqueue_.handler_ == nullptr);
-  ASSERT(enqueue_.reactable_ == nullptr);
-  enqueue_.handler_ = handler;
-  enqueue_.reactable_ = enqueue_.handler_->thread_->GetReactor()->Register(
-      enqueue_.reactive_semaphore_.GetFd(),
-      base::Bind(&Queue<T>::EnqueueCallbackInternal, base::Unretained(this), std::move(callback)),
-      base::Closure());
-}
-
-template <typename T>
-void Queue<T>::UnregisterEnqueue() {
-  Reactor* reactor = nullptr;
-  Reactor::Reactable* to_unregister = nullptr;
-  bool wait_for_unregister = false;
-  {
-    std::lock_guard<std::mutex> lock(mutex_);
-    ASSERT(enqueue_.reactable_ != nullptr);
-    reactor = enqueue_.handler_->thread_->GetReactor();
-    wait_for_unregister = (!enqueue_.handler_->thread_->IsSameThread());
-    to_unregister = enqueue_.reactable_;
-    enqueue_.reactable_ = nullptr;
-    enqueue_.handler_ = nullptr;
-  }
-  reactor->Unregister(to_unregister);
-  if (wait_for_unregister) {
-    reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
-  }
-}
-
-template <typename T>
-void Queue<T>::RegisterDequeue(Handler* handler, DequeueCallback callback) {
-  std::lock_guard<std::mutex> lock(mutex_);
-  ASSERT(dequeue_.handler_ == nullptr);
-  ASSERT(dequeue_.reactable_ == nullptr);
-  dequeue_.handler_ = handler;
-  dequeue_.reactable_ = dequeue_.handler_->thread_->GetReactor()->Register(
-      dequeue_.reactive_semaphore_.GetFd(), callback, base::Closure());
-}
-
-template <typename T>
-void Queue<T>::UnregisterDequeue() {
-  Reactor* reactor = nullptr;
-  Reactor::Reactable* to_unregister = nullptr;
-  bool wait_for_unregister = false;
-  {
-    std::lock_guard<std::mutex> lock(mutex_);
-    ASSERT(dequeue_.reactable_ != nullptr);
-    reactor = dequeue_.handler_->thread_->GetReactor();
-    wait_for_unregister = (!dequeue_.handler_->thread_->IsSameThread());
-    to_unregister = dequeue_.reactable_;
-    dequeue_.reactable_ = nullptr;
-    dequeue_.handler_ = nullptr;
-  }
-  reactor->Unregister(to_unregister);
-  if (wait_for_unregister) {
-    reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
-  }
-}
-
-template <typename T>
-std::unique_ptr<T> Queue<T>::TryDequeue() {
-  std::lock_guard<std::mutex> lock(mutex_);
-
-  if (queue_.empty()) {
-    return nullptr;
-  }
-
-  dequeue_.reactive_semaphore_.Decrease();
-
-  std::unique_ptr<T> data = std::move(queue_.front());
-  queue_.pop();
-
-  enqueue_.reactive_semaphore_.Increase();
-
-  return data;
-}
-
-template <typename T>
-void Queue<T>::EnqueueCallbackInternal(EnqueueCallback callback) {
-  std::unique_ptr<T> data = callback.Run();
-  ASSERT(data != nullptr);
-  std::lock_guard<std::mutex> lock(mutex_);
-  enqueue_.reactive_semaphore_.Decrease();
-  queue_.push(std::move(data));
-  dequeue_.reactive_semaphore_.Increase();
-}
diff --git a/system/gd/os/linux_generic/reactor_unittest.cc b/system/gd/os/linux_generic/reactor_unittest.cc
index 993dbfe..7893f55 100644
--- a/system/gd/os/linux_generic/reactor_unittest.cc
+++ b/system/gd/os/linux_generic/reactor_unittest.cc
@@ -93,7 +93,7 @@
   }
 
   void OnReadReady() {
-    LOG_INFO();
+    log::info("");
     uint64_t value = 0;
     auto read_result = eventfd_read(fd_, &value);
     log::info("value = {}", (int)value);
@@ -330,14 +330,14 @@
   auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
-  LOG_INFO();
+  log::info("");
   delete g_promise;
   g_promise = new std::promise<int>;
   future = g_promise->get_future();
   write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kUnregisterSampleReactable);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
-  LOG_INFO();
+  log::info("");
   reactor_->Stop();
   reactor_thread.join();
 
diff --git a/system/gd/os/log.h b/system/gd/os/log.h
index 437cba5..be42c68 100644
--- a/system/gd/os/log.h
+++ b/system/gd/os/log.h
@@ -33,15 +33,6 @@
 
 #if defined(FUZZ_TARGET)
 
-#define LOG_VERBOSE_INT(...)
-#define LOG_DEBUG_INT(...)
-#define LOG_INFO_INT(...)
-#define LOG_WARN_INT(...)
-
-#define LOG_ERROR_INT(...) do {     \
-  fprintf(stderr, __VA_ARGS__);     \
-} while (false)
-
 // for fuzz targets, we just
 // need to abort in this statement
 // to catch the bug
@@ -57,25 +48,6 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 
-#define LOG_VERBOSE_INT(fmt, args...)                                                     \
-  do {                                                                                    \
-    if (!__android_log_is_loggable(ANDROID_LOG_VERBOSE, LOG_TAG, ANDROID_LOG_INFO) &&     \
-        !__android_log_is_loggable(ANDROID_LOG_VERBOSE, "bluetooth", ANDROID_LOG_INFO)) { \
-      ALOGV(fmt, ##args);                                                                 \
-    }                                                                                     \
-  } while (false)
-
-#define LOG_DEBUG_INT(fmt, args...)                                                     \
-  do {                                                                                  \
-    if (!__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO) &&     \
-        !__android_log_is_loggable(ANDROID_LOG_DEBUG, "bluetooth", ANDROID_LOG_INFO)) { \
-      ALOGD(fmt, ##args);                                                               \
-    }                                                                                   \
-  } while (false)
-
-#define LOG_INFO_INT(fmt, args...) ALOGI(fmt, ##args)
-#define LOG_WARN_INT(fmt, args...) ALOGW(fmt, ##args)
-#define LOG_ERROR_INT(fmt, args...) ALOGE(fmt, ##args)
 #define LOG_ALWAYS_FATAL_INT(fmt, args...) do { \
   ALOGE(fmt, ##args);                           \
   abort();                                      \
@@ -88,28 +60,19 @@
 #define LOGWRAPPER(fmt, args...) VERBOSE_INFO(bluetooth, "bluetooth: " fmt, \
                                                ##args)
 
-#define LOG_VEBOSE_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_DEBUG_INT(...)  LOGWRAPPER(__VA_ARGS__)
-#define LOG_INFO_INT(...)   LOGWRAPPER(__VA_ARGS__)
-#define LOG_WARN_INT(...)   LOGWRAPPER(__VA_ARGS__)
-#define LOG_ERROR_INT(...)  LOGWRAPPER(__VA_ARGS__)
 #define LOG_ALWAYS_FATAL_INT(fmt, args...)                                          \
   do {                                                                              \
     fprintf(stderr, fmt "\n", ##args);                                              \
     abort();                                                                        \
   } while (false)
+
 #elif defined(TARGET_FLOSS) /* end of defined (ANDROID_EMULATOR) */
+
 #include "os/syslog.h"
 
 // Prefix the log with tag, file, line and function
 #define LOGWRAPPER(tag, fmt, args...) write_syslog(tag, LOG_TAG, "%s: " fmt, LOG_TAG, ##args)
 
-#define LOG_VERBOSE_INT(...) LOGWRAPPER(LOG_TAG_VERBOSE, __VA_ARGS__)
-#define LOG_DEBUG_INT(...) LOGWRAPPER(LOG_TAG_DEBUG, __VA_ARGS__)
-#define LOG_INFO_INT(...) LOGWRAPPER(LOG_TAG_INFO, __VA_ARGS__)
-#define LOG_WARN_INT(...) LOGWRAPPER(LOG_TAG_WARN, __VA_ARGS__)
-#define LOG_ERROR_INT(...) LOGWRAPPER(LOG_TAG_ERROR, __VA_ARGS__)
-
 #define LOG_ALWAYS_FATAL_INT(...)           \
   do {                                      \
     LOGWRAPPER(LOG_TAG_FATAL, __VA_ARGS__); \
@@ -148,12 +111,6 @@
         ##args);                                                                                                    \
   } while (false)
 
-#define LOG_VERBOSE_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_DEBUG_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_INFO_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_WARN_INT(...) LOGWRAPPER(__VA_ARGS__)
-#define LOG_ERROR_INT(...) LOGWRAPPER(__VA_ARGS__)
-
 #ifndef LOG_ALWAYS_FATAL
 #define LOG_ALWAYS_FATAL_INT(...) \
   do {                            \
@@ -169,6 +126,7 @@
 #define _LOG_SRC_FMT_STR "%s:%d - %s: "
 #define _PREPEND_SRC_LOC_IN_LOG(fmt, args...) \
   _LOG_SRC_FMT_STR fmt, __FILE__, __LINE__, __func__, ##args
+
 // ---------------------------------------------------------
 // All MACROs defined above are internal and should *not* be
 // used directly (use LOG_XXX defined below instead).
@@ -176,21 +134,6 @@
 // location of the log emitting statement, so far they are only used by
 // LogMsg, where the source locations is passed in.
 
-#define LOG_VERBOSE(fmt, args...)                                             \
-  LOG_VERBOSE_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
-#define LOG_DEBUG(fmt, args...)                                               \
-  LOG_DEBUG_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
-#define LOG_INFO(fmt, args...)                                                \
-  LOG_INFO_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
-#define LOG_WARN(fmt, args...)                                                \
-  LOG_WARN_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
-#define LOG_ERROR(fmt, args...)                                               \
-  LOG_ERROR_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
-
 #ifndef LOG_ALWAYS_FATAL
 #define LOG_ALWAYS_FATAL(fmt, args...)                                        \
   LOG_ALWAYS_FATAL_INT(_PREPEND_SRC_LOC_IN_LOG(fmt, ##args))
diff --git a/system/gd/os/queue.h b/system/gd/os/queue.h
index 57b04d5..820b35c 100644
--- a/system/gd/os/queue.h
+++ b/system/gd/os/queue.h
@@ -157,7 +157,104 @@
   common::OnceClosure callback_on_empty_;
 };
 
-#include "os/linux_generic/queue.tpp"
+template <typename T>
+Queue<T>::Queue(size_t capacity) : enqueue_(capacity), dequeue_(0){};
+
+template <typename T>
+Queue<T>::~Queue() {
+  ASSERT_LOG(enqueue_.handler_ == nullptr, "Enqueue is not unregistered");
+  ASSERT_LOG(dequeue_.handler_ == nullptr, "Dequeue is not unregistered");
+};
+
+template <typename T>
+void Queue<T>::RegisterEnqueue(Handler* handler, EnqueueCallback callback) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(enqueue_.handler_ == nullptr);
+  ASSERT(enqueue_.reactable_ == nullptr);
+  enqueue_.handler_ = handler;
+  enqueue_.reactable_ = enqueue_.handler_->thread_->GetReactor()->Register(
+      enqueue_.reactive_semaphore_.GetFd(),
+      base::Bind(&Queue<T>::EnqueueCallbackInternal, base::Unretained(this), std::move(callback)),
+      base::Closure());
+}
+
+template <typename T>
+void Queue<T>::UnregisterEnqueue() {
+  Reactor* reactor = nullptr;
+  Reactor::Reactable* to_unregister = nullptr;
+  bool wait_for_unregister = false;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(enqueue_.reactable_ != nullptr);
+    reactor = enqueue_.handler_->thread_->GetReactor();
+    wait_for_unregister = (!enqueue_.handler_->thread_->IsSameThread());
+    to_unregister = enqueue_.reactable_;
+    enqueue_.reactable_ = nullptr;
+    enqueue_.handler_ = nullptr;
+  }
+  reactor->Unregister(to_unregister);
+  if (wait_for_unregister) {
+    reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
+  }
+}
+
+template <typename T>
+void Queue<T>::RegisterDequeue(Handler* handler, DequeueCallback callback) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(dequeue_.handler_ == nullptr);
+  ASSERT(dequeue_.reactable_ == nullptr);
+  dequeue_.handler_ = handler;
+  dequeue_.reactable_ = dequeue_.handler_->thread_->GetReactor()->Register(
+      dequeue_.reactive_semaphore_.GetFd(), callback, base::Closure());
+}
+
+template <typename T>
+void Queue<T>::UnregisterDequeue() {
+  Reactor* reactor = nullptr;
+  Reactor::Reactable* to_unregister = nullptr;
+  bool wait_for_unregister = false;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT(dequeue_.reactable_ != nullptr);
+    reactor = dequeue_.handler_->thread_->GetReactor();
+    wait_for_unregister = (!dequeue_.handler_->thread_->IsSameThread());
+    to_unregister = dequeue_.reactable_;
+    dequeue_.reactable_ = nullptr;
+    dequeue_.handler_ = nullptr;
+  }
+  reactor->Unregister(to_unregister);
+  if (wait_for_unregister) {
+    reactor->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
+  }
+}
+
+template <typename T>
+std::unique_ptr<T> Queue<T>::TryDequeue() {
+  std::lock_guard<std::mutex> lock(mutex_);
+
+  if (queue_.empty()) {
+    return nullptr;
+  }
+
+  dequeue_.reactive_semaphore_.Decrease();
+
+  std::unique_ptr<T> data = std::move(queue_.front());
+  queue_.pop();
+
+  enqueue_.reactive_semaphore_.Increase();
+
+  return data;
+}
+
+template <typename T>
+void Queue<T>::EnqueueCallbackInternal(EnqueueCallback callback) {
+  std::unique_ptr<T> data = callback.Run();
+  ASSERT(data != nullptr);
+  std::lock_guard<std::mutex> lock(mutex_);
+  enqueue_.reactive_semaphore_.Decrease();
+  queue_.push(std::move(data));
+  dequeue_.reactive_semaphore_.Increase();
+}
 
 }  // namespace os
 }  // namespace bluetooth
diff --git a/system/gd/packet/parser/gen_cpp.cc b/system/gd/packet/parser/gen_cpp.cc
index 0d5014e..420a516 100644
--- a/system/gd/packet/parser/gen_cpp.cc
+++ b/system/gd/packet/parser/gen_cpp.cc
@@ -96,7 +96,6 @@
 #include "packet/packet_view.h"
 #include "packet/checksum_type_checker.h"
 #include "packet/custom_type_checker.h"
-#include "os/log.h"
 )";
 
   if (generate_fuzzing || generate_tests) {
@@ -105,6 +104,7 @@
 
 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
 #include "packet/raw_builder.h"
+#include <bluetooth/log.h>
 #endif
 )";
   }
diff --git a/system/gd/packet/parser/packet_def.cc b/system/gd/packet/parser/packet_def.cc
index eaf7c85..d91ab2d 100644
--- a/system/gd/packet/parser/packet_def.cc
+++ b/system/gd/packet/parser/packet_def.cc
@@ -484,8 +484,9 @@
   s << "public: ";
   s << "void CompareBytes(std::vector<uint8_t> captured_packet) {";
   s << name_ << "View view = " << name_ << "View::FromBytes(captured_packet);";
-  s << "if (!view.IsValid()) { LOG_INFO(\"Invalid Packet Bytes (size = %zu)\", view.size());";
-  s << "for (size_t i = 0; i < view.size(); i++) { LOG_INFO(\"%5zd:%02X\", i, *(view.begin() + i)); }}";
+  s << "if (!view.IsValid()) { log::info(\"Invalid Packet Bytes (size = {})\", view.size());";
+  s << "for (size_t i = 0; i < view.size(); i++) { log::info(\"{:5}:{:02x}\", i, *(view.begin() + "
+       "i)); }}";
   s << "ASSERT_TRUE(view.IsValid());";
   s << "auto packet = " << name_ << "Builder::FromView(view);";
   s << "std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();";
diff --git a/system/gd/rust/linux/stack/src/bluetooth_media.rs b/system/gd/rust/linux/stack/src/bluetooth_media.rs
index 112c267..631ede6 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_media.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_media.rs
@@ -1299,8 +1299,9 @@
             // or pre-exists, and that an app which disconnects from WebHID may not have trigger
             // the UHID_OUTPUT_NONE, we need to remove all pending HID calls on telephony use
             // release to keep lower HF layer in sync and not prevent A2DP streaming
-            self.hangup_call_impl();
-            self.phone_state_change("".into());
+            if self.hangup_call_impl() {
+                self.phone_state_change("".into());
+            }
         }
         self.telephony_callbacks.lock().unwrap().for_all_callbacks(|callback| {
             callback.on_telephony_use(address.to_string(), state);
diff --git a/system/main/shim/acl.cc b/system/main/shim/acl.cc
index 1980b6c..838ce45 100644
--- a/system/main/shim/acl.cc
+++ b/system/main/shim/acl.cc
@@ -900,7 +900,7 @@
     if (IsClassicAcl(handle)) {
       handle_to_classic_connection_map_[handle]->Flush();
     } else {
-      LOG_ERROR("handle %d is not a classic connection", handle);
+      log::error("handle {} is not a classic connection", handle);
     }
   }
 
@@ -1109,7 +1109,7 @@
                                     uint16_t max_ce_len) {
     auto connection = handle_to_le_connection_map_.find(handle);
     if (connection == handle_to_le_connection_map_.end()) {
-      LOG_WARN("Unknown le connection handle:0x%04x", handle);
+      log::warn("Unknown le connection handle:0x{:04x}", handle);
       return;
     }
     connection->second->UpdateConnectionParameters(conn_int_min, conn_int_max,
@@ -1798,36 +1798,6 @@
                    conn_timeout, min_ce_len, max_ce_len);
 }
 
-bool shim::legacy::Acl::HoldMode(uint16_t hci_handle, uint16_t max_interval,
-                                 uint16_t min_interval) {
-  handler_->CallOn(pimpl_.get(), &Acl::impl::HoldMode, hci_handle, max_interval,
-                   min_interval);
-  return false;  // TODO void
-}
-
-bool shim::legacy::Acl::SniffMode(uint16_t hci_handle, uint16_t max_interval,
-                                  uint16_t min_interval, uint16_t attempt,
-                                  uint16_t timeout) {
-  handler_->CallOn(pimpl_.get(), &Acl::impl::SniffMode, hci_handle,
-                   max_interval, min_interval, attempt, timeout);
-  return false;
-}
-
-bool shim::legacy::Acl::ExitSniffMode(uint16_t hci_handle) {
-  handler_->CallOn(pimpl_.get(), &Acl::impl::ExitSniffMode, hci_handle);
-  return false;
-}
-
-bool shim::legacy::Acl::SniffSubrating(uint16_t hci_handle,
-                                       uint16_t maximum_latency,
-                                       uint16_t minimum_remote_timeout,
-                                       uint16_t minimum_local_timeout) {
-  handler_->CallOn(pimpl_.get(), &Acl::impl::SniffSubrating, hci_handle,
-                   maximum_latency, minimum_remote_timeout,
-                   minimum_local_timeout);
-  return false;
-}
-
 void shim::legacy::Acl::LeSetDefaultSubrate(uint16_t subrate_min,
                                             uint16_t subrate_max,
                                             uint16_t max_latency,
diff --git a/system/main/shim/acl.h b/system/main/shim/acl.h
index ec659bd..d00b34e 100644
--- a/system/main/shim/acl.h
+++ b/system/main/shim/acl.h
@@ -26,7 +26,6 @@
 #include "hci/class_of_device.h"
 #include "main/shim/acl_legacy_interface.h"
 #include "main/shim/link_connection_interface.h"
-#include "main/shim/link_policy_interface.h"
 #include "os/handler.h"
 #include "packet/raw_builder.h"
 #include "types/raw_address.h"
@@ -37,8 +36,7 @@
 
 class Acl : public hci::acl_manager::ConnectionCallbacks,
             public hci::acl_manager::LeConnectionCallbacks,
-            public LinkConnectionInterface,
-            public LinkPolicyInterface {
+            public LinkConnectionInterface {
  public:
   Acl(os::Handler* handler, const acl_interface_t& acl_interface,
       uint8_t max_acceptlist_size, uint8_t max_address_resolution_size);
@@ -95,16 +93,6 @@
       const hci::AddressWithType& address_with_type);
   void ClearAddressResolution();
 
-  // LinkPolicyInterface
-  bool HoldMode(uint16_t hci_handle, uint16_t max_interval,
-                uint16_t min_interval) override;
-  bool SniffMode(uint16_t hci_handle, uint16_t max_interval,
-                 uint16_t min_interval, uint16_t attempt,
-                 uint16_t timeout) override;
-  bool ExitSniffMode(uint16_t hci_handle) override;
-  bool SniffSubrating(uint16_t hci_handle, uint16_t maximum_latency,
-                      uint16_t minimum_remote_timeout,
-                      uint16_t minimum_local_timeout) override;
   void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max,
                            uint16_t max_latency, uint16_t cont_num,
                            uint16_t sup_tout);
diff --git a/system/main/shim/distance_measurement_manager.cc b/system/main/shim/distance_measurement_manager.cc
index 31e835e..a5ad180 100644
--- a/system/main/shim/distance_measurement_manager.cc
+++ b/system/main/shim/distance_measurement_manager.cc
@@ -16,6 +16,7 @@
 
 #include "distance_measurement_manager.h"
 
+#include "bta/include/bta_ras_api.h"
 #include "btif/include/btif_common.h"
 #include "hci/distance_measurement_manager.h"
 #include "main/shim/entry.h"
@@ -104,6 +105,13 @@
             error_altitude_angle, static_cast<uint8_t>(method)));
   }
 
+  void OnRasFragmentReady(bluetooth::hci::Address address,
+                          uint16_t procedure_counter, bool is_last,
+                          std::vector<uint8_t> raw_data) {
+    bluetooth::ras::GetRasServer()->PushProcedureData(
+        bluetooth::ToRawAddress(address), procedure_counter, is_last, raw_data);
+  }
+
  private:
   ::DistanceMeasurementCallbacks* distance_measurement_callbacks_;
 };
diff --git a/system/main/shim/link_policy_interface.h b/system/main/shim/link_policy_interface.h
deleted file mode 100644
index 41423a1..0000000
--- a/system/main/shim/link_policy_interface.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-namespace bluetooth {
-namespace shim {
-
-class LinkPolicyInterface {
- public:
-  virtual bool HoldMode(uint16_t handle, uint16_t max_interval,
-                        uint16_t min_interval) = 0;
-  virtual bool SniffMode(uint16_t handle, uint16_t max_interval,
-                         uint16_t min_interval, uint16_t attempt,
-                         uint16_t timeout) = 0;
-  virtual bool ExitSniffMode(uint16_t handle) = 0;
-  virtual bool SniffSubrating(uint16_t handle, uint16_t maximum_latency,
-                              uint16_t minimum_remote_timeout,
-                              uint16_t minimum_local_timeout) = 0;
-  virtual ~LinkPolicyInterface() = default;
-};
-
-}  // namespace shim
-}  // namespace bluetooth
diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc
index 608b823..7430ecf 100644
--- a/system/main/shim/stack.cc
+++ b/system/main/shim/stack.cc
@@ -41,6 +41,7 @@
 #include "hci/vendor_specific_event_manager.h"
 #include "main/shim/acl.h"
 #include "main/shim/acl_legacy_interface.h"
+#include "main/shim/btm.h"
 #include "main/shim/distance_measurement_manager.h"
 #include "main/shim/entry.h"
 #include "main/shim/hci_layer.h"
@@ -60,6 +61,7 @@
 
 struct Stack::impl {
   legacy::Acl* acl_ = nullptr;
+  Btm* btm_ = nullptr;
 };
 
 Stack::Stack() { pimpl_ = std::make_shared<Stack::impl>(); }
@@ -151,8 +153,8 @@
   ASSERT_LOG(is_running_, "%s Gd stack not running", __func__);
   is_running_ = false;
 
-  delete btm_;
-  btm_ = nullptr;
+  delete pimpl_->btm_;
+  pimpl_->btm_ = nullptr;
 
   stack_handler_->Clear();
 
@@ -192,17 +194,10 @@
   return pimpl_->acl_;
 }
 
-LinkPolicyInterface* Stack::LinkPolicy() {
-  std::lock_guard<std::recursive_mutex> lock(mutex_);
-  ASSERT(is_running_);
-  ASSERT_LOG(pimpl_->acl_ != nullptr, "Acl shim layer has not been created");
-  return pimpl_->acl_;
-}
-
 Btm* Stack::GetBtm() {
   std::lock_guard<std::recursive_mutex> lock(mutex_);
   ASSERT(is_running_);
-  return btm_;
+  return pimpl_->btm_;
 }
 
 os::Handler* Stack::GetHandler() {
diff --git a/system/main/shim/stack.h b/system/main/shim/stack.h
index 25cba54..66b7462 100644
--- a/system/main/shim/stack.h
+++ b/system/main/shim/stack.h
@@ -19,8 +19,6 @@
 #include <functional>
 #include <mutex>
 
-#include "main/shim/btm.h"
-#include "main/shim/link_policy_interface.h"
 #include "module.h"
 #include "os/handler.h"
 #include "os/thread.h"
@@ -29,6 +27,9 @@
 // The shim layer implementation on the Gd stack side.
 namespace bluetooth {
 namespace shim {
+
+class Btm;
+
 namespace legacy {
 class Acl;
 };  // namespace legacy
@@ -55,7 +56,6 @@
   const StackManager* GetStackManager() const;
 
   legacy::Acl* GetAcl();
-  LinkPolicyInterface* LinkPolicy();
 
   Btm* GetBtm();
   os::Handler* GetHandler();
@@ -87,7 +87,6 @@
   bool is_running_ = false;
   os::Thread* stack_thread_ = nullptr;
   os::Handler* stack_handler_ = nullptr;
-  Btm* btm_ = nullptr;
   size_t num_modules_{0};
   void Start(ModuleList* modules);
 };
diff --git a/system/main/test/main_shim_test.cc b/system/main/test/main_shim_test.cc
index d79f59a..c1175b8 100644
--- a/system/main/test/main_shim_test.cc
+++ b/system/main/test/main_shim_test.cc
@@ -646,13 +646,7 @@
   run_all_jni_thread_task();
 }
 
-const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
 TEST_F(MainShimTest, DISABLED_LeShimAclConnection_local_disconnect) {
-  bluetooth::common::InitFlags::Load(test_flags);
   auto acl = MakeAcl();
   EXPECT_CALL(*test::mock_acl_manager_, CreateLeConnection(_, _)).Times(1);
 
diff --git a/system/stack/avrc/avrc_pars_tg.cc b/system/stack/avrc/avrc_pars_tg.cc
index 4a053c6..578fcc1 100644
--- a/system/stack/avrc/avrc_pars_tg.cc
+++ b/system/stack/avrc/avrc_pars_tg.cc
@@ -412,7 +412,7 @@
 
 #define RETURN_STATUS_IF_FALSE(_status_, _b_, _msg_, ...) \
   if (!(_b_)) {                                           \
-    LOG_VERBOSE(_msg_, ##__VA_ARGS__);                    \
+    log::verbose(_msg_, ##__VA_ARGS__);                   \
     return _status_;                                      \
   }
 
diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc
index c48147c..98ea0b5 100644
--- a/system/stack/btm/btm_ble_gap.cc
+++ b/system/stack/btm/btm_ble_gap.cc
@@ -2406,13 +2406,13 @@
       }
       if (has_advertising_flags && (local_flag & BTM_BLE_BREDR_NOT_SPT) == 0) {
         if (p_cur->ble_addr_type != BLE_ADDR_RANDOM) {
-          LOG_VERBOSE("NOT_BR_EDR support bit not set, treat device as DUMO");
+          log::verbose("NOT_BR_EDR support bit not set, treat device as DUMO");
           p_cur->device_type |= BT_DEVICE_TYPE_DUMO;
         } else {
-          LOG_VERBOSE("Random address, treat device as LE only");
+          log::verbose("Random address, treat device as LE only");
         }
       } else {
-        LOG_VERBOSE("NOT_BR/EDR support bit set, treat device as LE only");
+        log::verbose("NOT_BR/EDR support bit set, treat device as LE only");
       }
     }
   }
diff --git a/system/stack/test/gatt_connection_manager_test.cc b/system/stack/test/gatt_connection_manager_test.cc
index 039e829..afa178d 100644
--- a/system/stack/test/gatt_connection_manager_test.cc
+++ b/system/stack/test/gatt_connection_manager_test.cc
@@ -21,11 +21,6 @@
 
 using connection_manager::tAPP_ID;
 
-const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
 namespace {
 // convenience mock, for verifying acceptlist operations on lower layer are
 // actually scheduled
@@ -92,7 +87,6 @@
 namespace connection_manager {
 class BleConnectionManager : public testing::Test {
   void SetUp() override {
-    bluetooth::common::InitFlags::Load(test_flags);
     localAcceptlistMock = std::make_unique<AcceptlistMock>();
   }
 
diff --git a/system/stack/test/stack_acl_test.cc b/system/stack/test/stack_acl_test.cc
index 2d5d1be..59f8c8d 100644
--- a/system/stack/test/stack_acl_test.cc
+++ b/system/stack/test/stack_acl_test.cc
@@ -34,11 +34,6 @@
 tBTM_CB btm_cb;
 
 namespace {
-const char* test_flags[] = {
-    "INIT_logging_debug_enabled_for_all=true",
-    nullptr,
-};
-
 const RawAddress kRawAddress = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
 }  // namespace
 
@@ -54,7 +49,6 @@
  protected:
   void SetUp() override {
     reset_mock_function_count_map();
-    bluetooth::common::InitFlags::Load(test_flags);
     bluetooth::hci::testing::mock_controller_ = &controller_;
   }
   void TearDown() override {
diff --git a/system/test/mock/mock_bta_ras_server.cc b/system/test/mock/mock_bta_ras_server.cc
index 4d06c4f..ad6f7fb 100644
--- a/system/test/mock/mock_bta_ras_server.cc
+++ b/system/test/mock/mock_bta_ras_server.cc
@@ -16,13 +16,10 @@
 
 #include "bta/include/bta_ras_api.h"
 
-using bluetooth::ras::ProcedureDoneStatus;
-
 class MockRasServer : public bluetooth::ras::RasServer {
   void Initialize() override {}
   void PushProcedureData(RawAddress /* address */,
-                         uint16_t /* procedure_count */,
-                         ProcedureDoneStatus /* procedure_done_status */,
+                         uint16_t /* procedure_count */, bool /* is_last */,
                          std::vector<uint8_t> /* data */) override{};
 };
 
diff --git a/system/test/mock/mock_main_shim_acl.cc b/system/test/mock/mock_main_shim_acl.cc
index e8d5677..54817b1 100644
--- a/system/test/mock/mock_main_shim_acl.cc
+++ b/system/test/mock/mock_main_shim_acl.cc
@@ -163,35 +163,6 @@
   inc_func_call_count(__func__);
 }
 
-bool shim::legacy::Acl::HoldMode(uint16_t /* hci_handle */,
-                                 uint16_t /* max_interval */,
-                                 uint16_t /* min_interval */) {
-  inc_func_call_count(__func__);
-  return false;
-}
-
-bool shim::legacy::Acl::SniffMode(uint16_t /* hci_handle */,
-                                  uint16_t /* max_interval */,
-                                  uint16_t /* min_interval */,
-                                  uint16_t /* attempt */,
-                                  uint16_t /* timeout */) {
-  inc_func_call_count(__func__);
-  return false;
-}
-
-bool shim::legacy::Acl::ExitSniffMode(uint16_t /* hci_handle */) {
-  inc_func_call_count(__func__);
-  return false;
-}
-
-bool shim::legacy::Acl::SniffSubrating(uint16_t /* hci_handle */,
-                                       uint16_t /* maximum_latency */,
-                                       uint16_t /* minimum_remote_timeout */,
-                                       uint16_t /* minimum_local_timeout */) {
-  inc_func_call_count(__func__);
-  return false;
-}
-
 void shim::legacy::Acl::LeSetDefaultSubrate(uint16_t /* subrate_min */,
                                             uint16_t /* subrate_max */,
                                             uint16_t /* max_latency */,
diff --git a/tools/rootcanal/hal/bluetooth_hci.cc b/tools/rootcanal/hal/bluetooth_hci.cc
index 34cda65..7cea46b 100644
--- a/tools/rootcanal/hal/bluetooth_hci.cc
+++ b/tools/rootcanal/hal/bluetooth_hci.cc
@@ -19,6 +19,7 @@
 #include "bluetooth_hci.h"
 
 #include <cutils/properties.h>
+#include <log/log.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <string.h>
@@ -59,7 +60,7 @@
   void serviceDied(
       uint64_t /* cookie */,
       const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
-    LOG_ERROR("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
+    ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
     has_died_ = true;
     mHci->close();
   }
@@ -87,15 +88,15 @@
 Return<void> BluetoothHci::initialize_impl(
     const sp<V1_0::IBluetoothHciCallbacks>& cb,
     const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) {
-  LOG_INFO("%s", __func__);
+  ALOGI("%s", __func__);
   if (cb == nullptr) {
-    LOG_ERROR("cb == nullptr! -> Unable to call initializationComplete(ERR)");
+    ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
     return Void();
   }
 
   death_recipient_->setHasDied(false);
   auto link_ret = cb->linkToDeath(death_recipient_, 0);
-  ASSERT_LOG(link_ret.isOk(), "Error calling linkToDeath.");
+  ALOG_ASSERT(link_ret.isOk(), "Error calling linkToDeath.");
 
   test_channel_transport_.RegisterCommandHandler(
       [this](const std::string& name, const std::vector<std::string>& args) {
@@ -121,9 +122,9 @@
         hidl_vec<uint8_t> hci_event(packet->begin(), packet->end());
         auto ret = cb->hciEventReceived(hci_event);
         if (!ret.isOk()) {
-          LOG_ERROR("Error sending event callback");
+          ALOGE("Error sending event callback");
           if (!death_recipient_->getHasDied()) {
-            LOG_ERROR("Closing");
+            ALOGE("Closing");
             close();
           }
         }
@@ -134,9 +135,9 @@
         hidl_vec<uint8_t> acl_packet(packet->begin(), packet->end());
         auto ret = cb->aclDataReceived(acl_packet);
         if (!ret.isOk()) {
-          LOG_ERROR("Error sending acl callback");
+          ALOGE("Error sending acl callback");
           if (!death_recipient_->getHasDied()) {
-            LOG_ERROR("Closing");
+            ALOGE("Closing");
             close();
           }
         }
@@ -147,9 +148,9 @@
         hidl_vec<uint8_t> sco_packet(packet->begin(), packet->end());
         auto ret = cb->scoDataReceived(sco_packet);
         if (!ret.isOk()) {
-          LOG_ERROR("Error sending sco callback");
+          ALOGE("Error sending sco callback");
           if (!death_recipient_->getHasDied()) {
-            LOG_ERROR("Closing");
+            ALOGE("Closing");
             close();
           }
         }
@@ -161,9 +162,9 @@
           hidl_vec<uint8_t> iso_packet(packet->begin(), packet->end());
           auto ret = cb_1_1->isoDataReceived(iso_packet);
           if (!ret.isOk()) {
-            LOG_ERROR("Error sending iso callback");
+            ALOGE("Error sending iso callback");
             if (!death_recipient_->getHasDied()) {
-              LOG_ERROR("Closing");
+              ALOGE("Closing");
               close();
             }
           }
@@ -182,7 +183,7 @@
 
   // Send responses to logcat if the test channel is not configured.
   test_channel_.RegisterSendResponse([](const std::string& response) {
-    LOG_INFO("No test channel yet: %s", response.c_str());
+    ALOGI("No test channel yet: %s", response.c_str());
   });
 
   if (BtTestConsoleEnabled()) {
@@ -211,7 +212,7 @@
     });
   } else {
     // This should be configurable in the future.
-    LOG_INFO("Adding Beacons so the scan list is not empty.");
+    ALOGI("Adding Beacons so the scan list is not empty.");
     test_channel_.AddDevice({"beacon", "be:ac:10:00:00:01", "1000"});
     test_model_.AddDeviceToPhy(controller_index + 1, low_energy_phy_index);
     test_channel_.AddDevice({"beacon", "be:ac:10:00:00:02", "1000"});
@@ -224,28 +225,28 @@
     test_channel_.List({});
   }
 
-  unlink_cb_ = [this, cb](sp<BluetoothDeathRecipient>& death_recipient) {
+  unlink_cb_ = [cb](sp<BluetoothDeathRecipient>& death_recipient) {
     if (death_recipient->getHasDied())
-      LOG_INFO("Skipping unlink call, service died.");
+      ALOGI("Skipping unlink call, service died.");
     else {
       auto ret = cb->unlinkToDeath(death_recipient);
       if (!ret.isOk()) {
-        ASSERT_LOG(death_recipient_->getHasDied(),
-                   "Error calling unlink, but no death notification.");
+        ALOG_ASSERT(death_recipient_->getHasDied(),
+                    "Error calling unlink, but no death notification.");
       }
     }
   };
 
   auto init_ret = cb->initializationComplete(V1_0::Status::SUCCESS);
   if (!init_ret.isOk()) {
-    ASSERT_LOG(death_recipient_->getHasDied(),
-               "Error sending init callback, but no death notification.");
+    ALOG_ASSERT(death_recipient_->getHasDied(),
+                "Error sending init callback, but no death notification.");
   }
   return Void();
 }
 
 Return<void> BluetoothHci::close() {
-  LOG_INFO("%s", __func__);
+  ALOGI("%s", __func__);
   test_model_.Reset();
   return Void();
 }
@@ -296,11 +297,11 @@
 
 void BluetoothHci::SetUpHciServer(ConnectCallback connection_callback) {
   test_channel_.RegisterSendResponse([](const std::string& response) {
-    LOG_INFO("No HCI Response channel: %s", response.c_str());
+    ALOGI("No HCI Response channel: %s", response.c_str());
   });
 
   if (!remote_hci_transport_.SetUp(hci_socket_server_, connection_callback)) {
-    LOG_ERROR("Remote HCI channel SetUp failed.");
+    ALOGE("Remote HCI channel SetUp failed.");
     return;
   }
 }
@@ -309,7 +310,7 @@
   remote_link_layer_transport_.SetUp(link_socket_server_, connection_callback);
 
   test_channel_.RegisterSendResponse([](const std::string& response) {
-    LOG_INFO("No LinkLayer Response channel: %s", response.c_str());
+    ALOGI("No LinkLayer Response channel: %s", response.c_str());
   });
 }
 
@@ -324,7 +325,7 @@
   bool transport_configured = test_channel_transport_.SetUp(
       test_socket_server_, [this](std::shared_ptr<AsyncDataChannel> conn_fd,
                                   AsyncDataChannelServer*) {
-        LOG_INFO("Test channel connection accepted.");
+        ALOGI("Test channel connection accepted.");
         test_channel_.RegisterSendResponse(
             [this, conn_fd](const std::string& response) {
               test_channel_transport_.SendResponse(conn_fd, response);
@@ -336,15 +337,15 @@
         return false;
       });
   test_channel_.RegisterSendResponse([](const std::string& response) {
-    LOG_INFO("No test channel: %s", response.c_str());
+    ALOGI("No test channel: %s", response.c_str());
   });
 
   if (!transport_configured) {
-    LOG_ERROR("Test channel SetUp failed.");
+    ALOGE("Test channel SetUp failed.");
     return;
   }
 
-  LOG_INFO("Test channel SetUp() successful");
+  ALOGI("Test channel SetUp() successful");
 }
 
 /* Fallback to shared library if there is no service. */
diff --git a/tools/rootcanal/hal/bluetooth_hci.h b/tools/rootcanal/hal/bluetooth_hci.h
index 1bceda8..3745421 100644
--- a/tools/rootcanal/hal/bluetooth_hci.h
+++ b/tools/rootcanal/hal/bluetooth_hci.h
@@ -27,7 +27,6 @@
 #include "model/setup/test_model.h"
 #include "net/posix/posix_async_socket_connector.h"
 #include "net/posix/posix_async_socket_server.h"
-#include "os/log.h"
 
 namespace android {
 namespace hardware {
diff --git a/tools/rootcanal/hal/service.cc b/tools/rootcanal/hal/service.cc
index 7856dcf..cef8fe7 100644
--- a/tools/rootcanal/hal/service.cc
+++ b/tools/rootcanal/hal/service.cc
@@ -16,11 +16,10 @@
 
 #define LOG_TAG "android.hardware.bluetooth@1.1-service.sim"
 
-#include "os/log.h"
-
 #include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 #include <hidl/HidlSupport.h>
 #include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
 
 #include "bluetooth_hci.h"
 
@@ -37,5 +36,5 @@
   if (status == android::OK)
     joinRpcThreadpool();
   else
-    LOG_ERROR("Could not register as a service!");
+    ALOGE("Could not register as a service!");
 }
diff --git a/tools/rootcanal/include/os/log.h b/tools/rootcanal/include/os/log.h
deleted file mode 100644
index a1e46dd..0000000
--- a/tools/rootcanal/include/os/log.h
+++ /dev/null
@@ -1,33 +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.
- */
-
-// This header is currently needed for hci_packets.h
-// FIXME: Change hci_packets.h to not depend on os/log.h
-//        and remove this.
-#include "include/log.h"
-
-#define LOG_INFO(...)                                                       \
-  rootcanal::log::Log(rootcanal::log::Verbosity::kInfo, __FILE__, __LINE__, \
-                      "{}", fmt::sprintf(__VA_ARGS__))
-#define LOG_WARN(...)                                                          \
-  rootcanal::log::Log(rootcanal::log::Verbosity::kWarning, __FILE__, __LINE__, \
-                      "{}", fmt::sprintf(__VA_ARGS__))
-#define LOG_ERROR(...)                                                       \
-  rootcanal::log::Log(rootcanal::log::Verbosity::kError, __FILE__, __LINE__, \
-                      "{}", fmt::sprintf(__VA_ARGS__))
-#define LOG_ALWAYS_FATAL(...)                                                \
-  rootcanal::log::Log(rootcanal::log::Verbosity::kFatal, __FILE__, __LINE__, \
-                      "{}", fmt::sprintf(__VA_ARGS__))