Snap for 11510485 from 448440ff8193c6504a854c0294526419e3efe1f3 to 24Q2-release

Change-Id: I1b39e08556cd78329a941f6d37f4e66f5fb77844
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index b49597d..6b10df8 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -2269,6 +2269,34 @@
  * JNI Initialization
  */
 jint JNI_OnLoad(JavaVM* jvm, void* /* reserved */) {
+  /* Set the default logging level for the process using the tag
+   *  "log.tag.bluetooth" and/or "persist.log.tag.bluetooth" via the android
+   * logging framework.
+   */
+  const char* stack_default_log_tag = "bluetooth";
+  int default_prio = ANDROID_LOG_INFO;
+  if (__android_log_is_loggable(ANDROID_LOG_VERBOSE, stack_default_log_tag,
+                                default_prio)) {
+    __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE);
+    log::info("Set stack default log level to 'VERBOSE'");
+  } else if (__android_log_is_loggable(ANDROID_LOG_DEBUG, stack_default_log_tag,
+                                       default_prio)) {
+    __android_log_set_minimum_priority(ANDROID_LOG_DEBUG);
+    log::info("Set stack default log level to 'DEBUG'");
+  } else if (__android_log_is_loggable(ANDROID_LOG_INFO, stack_default_log_tag,
+                                       default_prio)) {
+    __android_log_set_minimum_priority(ANDROID_LOG_INFO);
+    log::info("Set stack default log level to 'INFO'");
+  } else if (__android_log_is_loggable(ANDROID_LOG_WARN, stack_default_log_tag,
+                                       default_prio)) {
+    __android_log_set_minimum_priority(ANDROID_LOG_WARN);
+    log::info("Set stack default log level to 'WARN'");
+  } else if (__android_log_is_loggable(ANDROID_LOG_ERROR, stack_default_log_tag,
+                                       default_prio)) {
+    __android_log_set_minimum_priority(ANDROID_LOG_ERROR);
+    log::info("Set stack default log level to 'ERROR'");
+  }
+
   JNIEnv* e;
   int status;
 
diff --git a/android/app/src/com/android/bluetooth/BluetoothEventLogger.java b/android/app/src/com/android/bluetooth/BluetoothEventLogger.java
index ed682a3..22604dd 100644
--- a/android/app/src/com/android/bluetooth/BluetoothEventLogger.java
+++ b/android/app/src/com/android/bluetooth/BluetoothEventLogger.java
@@ -60,15 +60,8 @@
 
     /** Add the event record and log debug message */
     public synchronized void logd(String tag, String msg) {
-        logd(true, tag, msg);
-    }
-
-    /** Add the event record and log debug message */
-    public synchronized void logd(boolean debug, String tag, String msg) {
         add(msg);
-        if (debug) {
-            Log.d(tag, msg);
-        }
+        Log.d(tag, msg);
     }
 
     /** Add the event record and log warning message */
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index 1a58e20..f4ba378 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -105,7 +105,11 @@
     A2dpStateMachine(BluetoothDevice device, A2dpService a2dpService,
                      A2dpNativeInterface a2dpNativeInterface, Looper looper) {
         super(TAG, looper);
-        setDbg(DBG);
+
+        // Let the logging framework enforce the log level. TAG is set above in the parent
+        // constructor.
+        setDbg(true);
+
         mDevice = device;
         mA2dpService = a2dpService;
         mA2dpNativeInterface = a2dpNativeInterface;
diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
index a7a814d..4c2ab41 100644
--- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
+++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
@@ -842,11 +842,11 @@
                 }
             }
             if (isActive != mAudioPlaybackIsActive) {
-                mAudioPlaybackStateLogger.logd(DEBUG, TAG, "onPlaybackConfigChanged: "
+                mAudioPlaybackStateLogger.logd(TAG, "onPlaybackConfigChanged: "
                         + (mAudioPlaybackIsActive ? "Active" : "Non-active") + " -> "
                         + (isActive ? "Active" : "Non-active"));
                 if (isActive) {
-                    mAudioPlaybackStateLogger.logd(DEBUG, TAG, "onPlaybackConfigChanged: "
+                    mAudioPlaybackStateLogger.logd(TAG, "onPlaybackConfigChanged: "
                             + "active config: " + activeConfig);
                 }
                 mAudioPlaybackIsActive = isActive;
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
index 0e33c42..443e5f9 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
@@ -457,7 +457,6 @@
         BluetoothDevice activeDevice = getA2dpActiveDevice();
         MediaPlayerWrapper player = mMediaPlayerList.getActivePlayer();
         mMediaKeyEventLogger.logd(
-                DEBUG,
                 TAG,
                 "sendMediaKeyEvent:"
                         + " device="
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
index 780b00d..9599c86 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
@@ -179,7 +179,7 @@
 
     void setVolume(@NonNull BluetoothDevice device, int avrcpVolume) {
         int deviceVolume = avrcpToSystemVolume(avrcpVolume);
-        mVolumeEventLogger.logd(DEBUG, TAG, "setVolume:"
+        mVolumeEventLogger.logd(TAG, "setVolume:"
                         + " device=" + device
                         + " avrcpVolume=" + avrcpVolume
                         + " deviceVolume=" + deviceVolume
@@ -196,7 +196,7 @@
             return;
         }
         int avrcpVolume = systemToAvrcpVolume(deviceVolume);
-        mVolumeEventLogger.logd(DEBUG, TAG, "sendVolumeChanged:"
+        mVolumeEventLogger.logd(TAG, "sendVolumeChanged:"
                         + " device=" + device
                         + " avrcpVolume=" + avrcpVolume
                         + " deviceVolume=" + deviceVolume
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 65e5e0d..3f5d5f5 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -336,7 +336,7 @@
                 }
             }
         }
-        sEventLogger.logd(DBG, TAG, "Broadcast Source Unsynced: scanDelegator= " + scanDelegator
+        sEventLogger.logd(TAG, "Broadcast Source Unsynced: scanDelegator= " + scanDelegator
                 + ", syncHandle= " + syncHandle);
     }
 
@@ -354,7 +354,7 @@
                 mActiveSourceMap.get(scanDelegator).add(syncHandle);
             }
         }
-        sEventLogger.logd(DBG, TAG, "Broadcast Source Synced: scanDelegator= " + scanDelegator
+        sEventLogger.logd(TAG, "Broadcast Source Synced: scanDelegator= " + scanDelegator
                 + ", syncHandle= " + syncHandle);
     }
 
@@ -895,7 +895,7 @@
                 return;
             }
 
-            sEventLogger.logd(DBG, TAG, "Broadcast timeout: " + mBroadcastId);
+            sEventLogger.logd(TAG, "Broadcast timeout: " + mBroadcastId);
             mLocalBroadcastReceivers.remove(mBroadcastId);
             leAudioService.stopBroadcast(mBroadcastId);
         }
@@ -983,7 +983,6 @@
         }
 
         sEventLogger.logd(
-                DBG,
                 TAG,
                 "connectionStateChanged: fromState= "
                         + BluetoothProfile.getConnectionStateName(fromState)
@@ -1298,7 +1297,7 @@
                             | ((broadcastIdArray[1] & 0xff) << 8)
                             | (broadcastIdArray[0] & 0xff));
 
-                    sEventLogger.logd(DBG, TAG, "Broadcast Source Found: Broadcast ID: "
+                    sEventLogger.logd(TAG, "Broadcast Source Found: Broadcast ID: "
                             + broadcastId);
 
                     if (broadcastId != BassConstants.INVALID_BROADCAST_ID
@@ -1343,7 +1342,7 @@
                                 serviceData, serviceDataMask).build());
             }
             scanner.startScan(filters, settings, mSearchScanCallback);
-            sEventLogger.logd(DBG, TAG, "startSearchingForSources");
+            sEventLogger.logd(TAG, "startSearchingForSources");
             mCallbacks.notifySearchStarted(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
         }
     }
@@ -1371,7 +1370,7 @@
             }
             scanner.stopScan(mSearchScanCallback);
             mSearchScanCallback = null;
-            sEventLogger.logd(DBG, TAG, "stopSearchingForSources");
+            sEventLogger.logd(TAG, "stopSearchingForSources");
             mCallbacks.notifySearchStopped(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
         }
     }
@@ -1402,7 +1401,7 @@
         }
 
         synchronized (mStateMachines) {
-            sEventLogger.logd(DBG, TAG, "Select Broadcast Source");
+            sEventLogger.logd(TAG, "Select Broadcast Source");
 
             BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
             Message message = stateMachine.obtainMessage(
@@ -1474,7 +1473,6 @@
                 Integer sourceId = getSourceIdToRemove(device);
                 if (sourceId != BassConstants.INVALID_SOURCE_ID) {
                     sEventLogger.logd(
-                            DBG,
                             TAG,
                             "Switch Broadcast Source: device: "
                                     + device
@@ -1534,7 +1532,6 @@
             }
 
             sEventLogger.logd(
-                    DBG,
                     TAG,
                     "Add Broadcast Source: device: "
                             + device
@@ -1547,7 +1544,7 @@
             message.obj = sourceMetadata;
             stateMachine.sendMessage(message);
             if (code != null && code.length != 0) {
-                sEventLogger.logd(DBG, TAG, "Set Broadcast Code (Add Source context)");
+                sEventLogger.logd(TAG, "Set Broadcast Code (Add Source context)");
 
                 message = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
                 message.obj = sourceMetadata;
@@ -1619,7 +1616,6 @@
             }
 
             sEventLogger.logd(
-                    DBG,
                     TAG,
                     "Modify Broadcast Source: device: "
                             + device
@@ -1635,7 +1631,7 @@
             message.obj = updatedMetadata;
             stateMachine.sendMessage(message);
             if (code != null && code.length != 0) {
-                sEventLogger.logd(DBG, TAG, "Set Broadcast Code (Modify Source context)");
+                sEventLogger.logd(TAG, "Set Broadcast Code (Modify Source context)");
                 message = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
                 message.obj = updatedMetadata;
                 message.arg1 = BassClientStateMachine.ARGTYPE_METADATA;
@@ -1688,7 +1684,6 @@
                     stateMachine.getCurrentBroadcastMetadata(sourceId);
             if (metaData != null && stateMachine.isSyncedToTheSource(sourceId)) {
                 sEventLogger.logd(
-                        DBG,
                         TAG,
                         "Remove Broadcast Source(Force lost PA sync): device: "
                                 + device
@@ -1708,7 +1703,6 @@
             }
 
             sEventLogger.logd(
-                    DBG,
                     TAG,
                     "Remove Broadcast Source: device: " + device + ", sourceId: " + sourceId);
 
@@ -1813,7 +1807,7 @@
 
         if (store && !mPausedBroadcastSinks.isEmpty()) {
             Log.w(TAG, "stopSourceReceivers(), paused broadcast sinks are replaced");
-            sEventLogger.logd(DBG, TAG, "Clear broadcast sinks paused cache");
+            sEventLogger.logd(TAG, "Clear broadcast sinks paused cache");
             mPausedBroadcastSinks.clear();
         }
 
@@ -1830,7 +1824,7 @@
                 }
 
                 if (store && !mPausedBroadcastSinks.contains(device)) {
-                    sEventLogger.logd(DBG, TAG, "Add broadcast sink to paused cache: " + device);
+                    sEventLogger.logd(TAG, "Add broadcast sink to paused cache: " + device);
                     mPausedBroadcastSinks.add(device);
                 }
 
@@ -1993,29 +1987,29 @@
 
     /** Request receivers to suspend broadcast sources synchronization */
     public void suspendReceiversSourceSynchronization(int broadcastId) {
-        sEventLogger.logd(DBG, TAG, "Suspend receivers source synchronization: " + broadcastId);
+        sEventLogger.logd(TAG, "Suspend receivers source synchronization: " + broadcastId);
         stopSourceReceivers(broadcastId, true);
     }
 
     /** Request all receivers to suspend broadcast sources synchronization */
     public void suspendAllReceiversSourceSynchronization() {
-        sEventLogger.logd(DBG, TAG, "Suspend all receivers source synchronization");
+        sEventLogger.logd(TAG, "Suspend all receivers source synchronization");
         stopSourceReceivers(BassConstants.INVALID_BROADCAST_ID, true);
     }
 
     /** Request receivers to stop broadcast sources synchronization and remove them */
     public void stopReceiversSourceSynchronization(int broadcastId) {
-        sEventLogger.logd(DBG, TAG, "Stop receivers source synchronization: " + broadcastId);
+        sEventLogger.logd(TAG, "Stop receivers source synchronization: " + broadcastId);
         stopSourceReceivers(broadcastId, false);
     }
 
     /** Request receivers to resume broadcast source synchronization */
     public void resumeReceiversSourceSynchronization() {
-        sEventLogger.logd(DBG, TAG, "Resume receivers source synchronization");
+        sEventLogger.logd(TAG, "Resume receivers source synchronization");
 
         while (!mPausedBroadcastSinks.isEmpty()) {
             BluetoothDevice sink = mPausedBroadcastSinks.remove();
-            sEventLogger.logd(DBG, TAG, "Remove broadcast sink from paused cache: " + sink);
+            sEventLogger.logd(TAG, "Remove broadcast sink from paused cache: " + sink);
             BluetoothLeBroadcastMetadata metadata = mBroadcastMetadataMap.get(sink);
 
             if (metadata != null) {
@@ -2062,7 +2056,7 @@
                 }
 
                 if (mLocalBroadcastReceivers.remove(broadcastId) != null) {
-                    sEventLogger.logd(DBG, TAG, "Broadcast ID: " + broadcastId + ", stopped");
+                    sEventLogger.logd(TAG, "Broadcast ID: " + broadcastId + ", stopped");
                 }
                 break;
             case BROADCAST_STATE_CONFIGURING:
@@ -2226,7 +2220,7 @@
         }
 
         void notifySearchStarted(int reason) {
-            sEventLogger.logd(DBG, TAG, "notifySearchStarted: reason: " + reason);
+            sEventLogger.logd(TAG, "notifySearchStarted: reason: " + reason);
             obtainMessage(MSG_SEARCH_STARTED, reason, 0).sendToTarget();
         }
 
@@ -2236,7 +2230,7 @@
         }
 
         void notifySearchStopped(int reason) {
-            sEventLogger.logd(DBG, TAG, "notifySearchStopped: reason: " + reason);
+            sEventLogger.logd(TAG, "notifySearchStopped: reason: " + reason);
             obtainMessage(MSG_SEARCH_STOPPED, reason, 0).sendToTarget();
         }
 
@@ -2247,7 +2241,6 @@
 
         void notifySourceFound(BluetoothLeBroadcastMetadata source) {
             sEventLogger.logd(
-                    DBG,
                     TAG,
                     "invokeCallback: MSG_SOURCE_FOUND"
                             + ", source: "
@@ -2268,7 +2261,6 @@
             sService.localNotifySourceAdded(sink, recvState);
 
             sEventLogger.logd(
-                    DBG,
                     TAG,
                     "notifySourceAdded: "
                             + "source: "
@@ -2292,7 +2284,6 @@
 
         void notifySourceModified(BluetoothDevice sink, int sourceId, int reason) {
             sEventLogger.logd(
-                    DBG,
                     TAG,
                     "notifySourceModified: "
                             + "source: "
@@ -2313,7 +2304,6 @@
 
         void notifySourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
             sEventLogger.logd(
-                    DBG,
                     TAG,
                     "notifySourceRemoved: "
                             + "source: "
diff --git a/android/app/src/com/android/bluetooth/gatt/ContextMap.java b/android/app/src/com/android/bluetooth/gatt/ContextMap.java
index f8f7ca8..3457ed7 100644
--- a/android/app/src/com/android/bluetooth/gatt/ContextMap.java
+++ b/android/app/src/com/android/bluetooth/gatt/ContextMap.java
@@ -50,7 +50,6 @@
  * This class manages application callbacks and keeps track of GATT connections.
  * @hide
  */
-@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
 public class ContextMap<C, T> {
     private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap";
 
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index 1ef61f3..4f4dab7 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -2880,8 +2880,7 @@
     // Check and deliver scan results for different scan clients.
     private void deliverBatchScan(ScanClient client, Set<ScanResult> allResults)
             throws RemoteException {
-        TransitionalScanHelper.ScannerMap.App app =
-                mTransitionalScanHelper.getScannerMap().getById(client.scannerId);
+        ContextMap.App app = mTransitionalScanHelper.getScannerMap().getById(client.scannerId);
         if (app == null) {
             return;
         }
@@ -3089,8 +3088,7 @@
     }
 
     void onScanParamSetupCompleted(int status, int scannerId) throws RemoteException {
-        TransitionalScanHelper.ScannerMap.App app =
-                mTransitionalScanHelper.getScannerMap().getById(scannerId);
+        ContextMap.App app = mTransitionalScanHelper.getScannerMap().getById(scannerId);
         if (app == null || app.callback == null) {
             Log.e(TAG, "Advertise app or callback is null");
             return;
@@ -3317,8 +3315,7 @@
         scanClient.associatedDevices = getAssociatedDevices(callingPackage);
 
         AppScanStats app = mTransitionalScanHelper.getScannerMap().getAppScanStatsById(scannerId);
-        TransitionalScanHelper.ScannerMap.App cbApp =
-                mTransitionalScanHelper.getScannerMap().getById(scannerId);
+        ContextMap.App cbApp = mTransitionalScanHelper.getScannerMap().getById(scannerId);
         if (app != null) {
             scanClient.stats = app;
             boolean isFilteredScan = (filters != null) && !filters.isEmpty();
@@ -3370,7 +3367,7 @@
             return;
         }
 
-        TransitionalScanHelper.ScannerMap.App app =
+        ContextMap.App app =
                 mTransitionalScanHelper.getScannerMap().add(uuid, null, null, piInfo, this);
 
         app.mUserHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid());
@@ -3479,8 +3476,7 @@
         }
         PendingIntentInfo pii = new PendingIntentInfo();
         pii.intent = intent;
-        TransitionalScanHelper.ScannerMap.App app =
-                mTransitionalScanHelper.getScannerMap().getByContextInfo(pii);
+        ContextMap.App app = mTransitionalScanHelper.getScannerMap().getByContextInfo(pii);
         if (VDBG) {
             Log.d(TAG, "stopScan(PendingIntent): app found = " + app);
         }
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index e63d83b..38db4dc 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -207,8 +207,11 @@
             HeadsetService headsetService, AdapterService adapterService,
             HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) {
         super(TAG, Objects.requireNonNull(looper, "looper cannot be null"));
-        // Enable/Disable StateMachine debug logs
-        setDbg(DBG);
+
+        // Let the logging framework enforce the log level. TAG is set above in the parent
+        // constructor.
+        setDbg(true);
+
         mDevice = Objects.requireNonNull(device, "device cannot be null");
         mHeadsetService = Objects.requireNonNull(headsetService, "headsetService cannot be null");
         mNativeInterface =
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 36aa3b9..8ccf2ee 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -79,8 +79,7 @@
 import com.android.bluetooth.btservice.ServiceFactory;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.csip.CsipSetCoordinatorService;
-import com.android.bluetooth.flags.FeatureFlags;
-import com.android.bluetooth.flags.FeatureFlagsImpl;
+import com.android.bluetooth.flags.Flags;
 import com.android.bluetooth.hap.HapClientService;
 import com.android.bluetooth.hfp.HeadsetService;
 import com.android.bluetooth.mcp.McpService;
@@ -91,6 +90,7 @@
 import com.android.modules.utils.SynchronousResultReceiver;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -137,6 +137,16 @@
     public static final String BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_GROUP_ID =
             "bluetooth_le_broadcast_fallback_active_group_id";
 
+    /**
+     * Per PBP 1.0 4.3. High Quality Public Broadcast Audio, Broadcast HIGH quality audio configs
+     * are with sampling frequency 48khz
+     */
+    private static final BluetoothLeAudioCodecConfig BROADCAST_HIGH_QUALITY_CONFIG =
+            new BluetoothLeAudioCodecConfig.Builder()
+                    .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
+                    .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000)
+                    .build();
+
     private AdapterService mAdapterService;
     private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
@@ -149,7 +159,6 @@
     private final ReentrantReadWriteLock mGroupReadWriteLock = new ReentrantReadWriteLock();
     private final Lock mGroupReadLock = mGroupReadWriteLock.readLock();
     private final Lock mGroupWriteLock = mGroupReadWriteLock.writeLock();
-    private FeatureFlags mFeatureFlags;
     ServiceFactory mServiceFactory = new ServiceFactory();
 
     LeAudioNativeInterface mLeAudioNativeInterface;
@@ -200,14 +209,12 @@
 
     public LeAudioService(Context ctx) {
         super(ctx);
-        mFeatureFlags = new FeatureFlagsImpl();
     }
 
     @VisibleForTesting
-    LeAudioService(Context ctx, LeAudioNativeInterface nativeInterface, FeatureFlags featureFlags) {
+    LeAudioService(Context ctx, LeAudioNativeInterface nativeInterface) {
         super(ctx);
         mLeAudioNativeInterface = nativeInterface;
-        mFeatureFlags = featureFlags;
     }
 
     private class LeAudioGroupDescriptor {
@@ -467,7 +474,7 @@
                     sm.cleanup();
                 }
             } finally {
-                if (mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+                if (Flags.leaudioApiSynchronizedBlockFix()) {
                     // Upgrade to write lock
                     groupMutexUnlock(/* isReadOnly */ true);
                     groupMutexLock(/* isReadOnly */ false);
@@ -556,11 +563,6 @@
         sLeAudioService = instance;
     }
 
-    @VisibleForTesting
-    void setFeatureFlags(FeatureFlags featureFlags) {
-        mFeatureFlags = featureFlags;
-    }
-
     VolumeControlService getVolumeControlService() {
         if (mVolumeControlService == null) {
             mVolumeControlService = mServiceFactory.getVolumeControlService();
@@ -650,7 +652,7 @@
                 return false;
             }
 
-            if (!mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+            if (!Flags.leaudioApiSynchronizedBlockFix()) {
                 sm.sendMessage(LeAudioStateMachine.CONNECT);
             }
 
@@ -658,7 +660,7 @@
             groupMutexUnlock(/* isReadOnly */ false);
         }
 
-        if (mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+        if (Flags.leaudioApiSynchronizedBlockFix()) {
             sm.sendMessage(LeAudioStateMachine.CONNECT);
         }
 
@@ -707,7 +709,7 @@
      * @return true if profile disconnected, false if device not connected over LE Audio
      */
     public boolean disconnect(BluetoothDevice device) {
-        if (mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+        if (Flags.leaudioApiSynchronizedBlockFix()) {
             return disconnectV2(device);
         }
 
@@ -995,7 +997,7 @@
             Log.i(TAG, "Unicast group is active, queueing Broadcast creation, while the Unicast"
                         + " group is deactivated.");
             mCreateBroadcastQueue.add(broadcastSettings);
-            if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies()) {
+            if (Flags.leaudioBroadcastAudioHandoverPolicies()) {
                 mLeAudioNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK,
                         true);
             }
@@ -1026,16 +1028,52 @@
         Log.i(TAG, "createBroadcast: isEncrypted=" + (isEncrypted ? "true" : "false"));
 
         mAwaitingBroadcastCreateResponse = true;
-        mLeAudioBroadcasterNativeInterface.createBroadcast(broadcastSettings.isPublicBroadcast(),
-                broadcastSettings.getBroadcastName(), broadcastCode,
+        mLeAudioBroadcasterNativeInterface.createBroadcast(
+                broadcastSettings.isPublicBroadcast(),
+                broadcastSettings.getBroadcastName(),
+                broadcastCode,
                 publicMetadata == null ? null : publicMetadata.getRawMetadata(),
-                settingsList.stream()
-                        .mapToInt(s -> s.getPreferredQuality()).toArray(),
+                getBroadcastAudioQualityPerSinkCapabilities(settingsList),
                 settingsList.stream()
                         .map(s -> s.getContentMetadata().getRawMetadata())
                         .toArray(byte[][]::new));
     }
 
+    private int[] getBroadcastAudioQualityPerSinkCapabilities(
+            List<BluetoothLeBroadcastSubgroupSettings> settingsList) {
+        int[] preferredQualityArray =
+                settingsList.stream().mapToInt(s -> s.getPreferredQuality()).toArray();
+
+        BassClientService bassClientService = getBassClientService();
+        if (bassClientService == null) {
+            return preferredQualityArray;
+        }
+
+        for (BluetoothDevice sink : bassClientService.getConnectedDevices()) {
+            int groupId = getGroupId(sink);
+            if (groupId == LE_AUDIO_GROUP_ID_INVALID) {
+                continue;
+            }
+
+            BluetoothLeAudioCodecStatus codecStatus = getCodecStatus(groupId);
+            if (codecStatus != null
+                    && !codecStatus.isInputCodecConfigSelectable(BROADCAST_HIGH_QUALITY_CONFIG)) {
+                // If any sink device does not support high quality audio config,
+                // set all subgroup audio quality to standard quality for now before multi codec
+                // config support is ready
+                Log.i(
+                        TAG,
+                        "Sink device doesn't support HIGH broadcast audio quality, use STANDARD"
+                                + " quality");
+                Arrays.fill(
+                        preferredQualityArray,
+                        BluetoothLeBroadcastSubgroupSettings.QUALITY_STANDARD);
+                break;
+            }
+        }
+        return preferredQualityArray;
+    }
+
     /**
      * Start LeAudio Broadcast instance.
      * @param broadcastId broadcast instance identifier
@@ -1150,7 +1188,7 @@
         }
 
         if (DBG) Log.d(TAG, "destroyBroadcast");
-        if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies()) {
+        if (Flags.leaudioBroadcastAudioHandoverPolicies()) {
             mLeAudioNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK, false);
         }
         mLeAudioBroadcasterNativeInterface.destroyBroadcast(broadcastId);
@@ -1766,8 +1804,7 @@
             if (notifyAndUpdateInactiveOutDeviceOnly
                     && ((newSupportedAudioDirections & AUDIO_DIRECTION_INPUT_BIT) != 0)) {
                 newInDevice = getLeadDeviceForTheGroup(groupId);
-            } else if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies()
-                    && wasSetSinkListeningMode()) {
+            } else if (Flags.leaudioBroadcastAudioHandoverPolicies() && wasSetSinkListeningMode()) {
                 mLeAudioNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK,
                         false);
             }
@@ -1970,7 +2007,7 @@
             return false;
         }
 
-        if (!mFeatureFlags.audioRoutingCentralization()) {
+        if (!Flags.audioRoutingCentralization()) {
             // If AUDIO_ROUTING_CENTRALIZATION, this will be checked inside AudioRoutingManager.
             if (Utils.isDualModeAudioEnabled()) {
                 if (!mAdapterService.isAllSupportedClassicAudioProfilesActive(device)) {
@@ -2176,7 +2213,7 @@
                                 1);
                 break;
             case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP:
-                if (mFeatureFlags.leaudioUnicastInactivateDeviceBasedOnContext()) {
+                if (Flags.leaudioUnicastInactivateDeviceBasedOnContext()) {
                     LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId);
                     if (groupDescriptor != null && groupDescriptor.mIsActive) {
                         Log.i(
@@ -2230,7 +2267,7 @@
              */
             boolean leaveConnectedInputDevice = false;
             Integer newDirections = AUDIO_DIRECTION_NONE;
-            if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies()
+            if (Flags.leaudioBroadcastAudioHandoverPolicies()
                     && (!mCreateBroadcastQueue.isEmpty()
                             || mBroadcastIdDeactivatedForUnicastTransition.isPresent())) {
                 leaveConnectedInputDevice = true;
@@ -2928,7 +2965,7 @@
             }
 
             // Notify broadcast assistant
-            if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies()) {
+            if (Flags.leaudioBroadcastAudioHandoverPolicies()) {
                 if (bassClientService != null) {
                     bassClientService.notifyBroadcastStateChanged(descriptor.mState, broadcastId);
                 }
@@ -2988,11 +3025,7 @@
 
         sm =
                 LeAudioStateMachine.make(
-                        device,
-                        this,
-                        mLeAudioNativeInterface,
-                        mStateMachinesThread.getLooper(),
-                        mFeatureFlags);
+                        device, this, mLeAudioNativeInterface, mStateMachinesThread.getLooper());
         descriptor.mStateMachine = sm;
         return sm;
     }
@@ -3049,14 +3082,14 @@
                 }
             } finally {
                 // Reduce size of critical section when this feature is enabled
-                if (mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+                if (Flags.leaudioApiSynchronizedBlockFix()) {
                     groupMutexUnlock(/* isReadOnly */ true);
                 }
             }
             removeStateMachine(device);
             removeAuthorizationInfoForRelatedProfiles(device);
         } finally {
-            if (!mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+            if (!Flags.leaudioApiSynchronizedBlockFix()) {
                 groupMutexUnlock(/* isReadOnly */ true);
             }
         }
@@ -3086,7 +3119,7 @@
                 sm.cleanup();
                 descriptor.mStateMachine = null;
             } finally {
-                if (mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+                if (Flags.leaudioApiSynchronizedBlockFix()) {
                     // Upgrade to write lock
                     groupMutexUnlock(/* isReadOnly */ true);
                     groupMutexLock(/* isReadOnly */ false);
@@ -3232,7 +3265,7 @@
      * Process a change for disconnection of a device.
      */
     public synchronized void deviceDisconnected(BluetoothDevice device, boolean hasFallbackDevice) {
-        if (mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+        if (Flags.leaudioApiSynchronizedBlockFix()) {
             deviceDisconnectedV2(device, hasFallbackDevice);
             return;
         }
@@ -3388,8 +3421,7 @@
         }
 
         /* For setting inCall mode */
-        if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies() && inCall
-                && !areBroadcastsAllStopped()) {
+        if (Flags.leaudioBroadcastAudioHandoverPolicies() && inCall && !areBroadcastsAllStopped()) {
             mQueuedInCallValue = Optional.of(true);
 
             /* Request activation of unicast group */
@@ -3402,7 +3434,8 @@
         mLeAudioNativeInterface.setInCall(inCall);
 
         /* For clearing inCall mode */
-        if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies() && !inCall
+        if (Flags.leaudioBroadcastAudioHandoverPolicies()
+                && !inCall
                 && mBroadcastIdDeactivatedForUnicastTransition.isPresent()) {
             handleUnicastStreamStatusChange(
                     LeAudioStackEvent.DIRECTION_SINK,
@@ -3640,7 +3673,7 @@
     }
 
     void removeAuthorizationInfoForRelatedProfiles(BluetoothDevice device) {
-        if (!mFeatureFlags.leaudioMcsTbsAuthorizationRebondFix()) {
+        if (!Flags.leaudioMcsTbsAuthorizationRebondFix()) {
             Log.i(TAG, "leaudio_mcs_tbs_authorization_rebond_fix is disabled");
             return;
         }
@@ -3678,7 +3711,7 @@
                     return;
                 }
             } finally {
-                if (!mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+                if (!Flags.leaudioApiSynchronizedBlockFix()) {
                     // Keep previous behavior where a lock is released and acquired immediately
                     groupMutexUnlock(/* isReadOnly */ true);
                     groupMutexLock(/* isReadOnly */ true);
@@ -3821,7 +3854,7 @@
                         || Objects.equals(device, mActiveAudioInDevice)) {
                     handleGroupTransitToInactive(groupId);
                 }
-                if (!mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+                if (!Flags.leaudioApiSynchronizedBlockFix()) {
                     mGroupDescriptors.remove(groupId);
                 }
 
@@ -3834,7 +3867,7 @@
             groupMutexUnlock(/* isReadOnly */ true);
         }
 
-        if (isGroupEmpty && mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+        if (isGroupEmpty && Flags.leaudioApiSynchronizedBlockFix()) {
             groupMutexLock(/* isReadOnly */ false);
             try {
                 mGroupDescriptors.remove(groupId);
@@ -4366,7 +4399,7 @@
                 Objects.requireNonNull(receiver, "receiver cannot be null");
                 LeAudioService service = getService(source);
                 if (service != null) {
-                    if (service.mFeatureFlags.audioRoutingCentralization()) {
+                    if (Flags.audioRoutingCentralization()) {
                         ((AudioRoutingManager) service.mAdapterService.getActiveDeviceManager())
                                 .activateDeviceProfile(device, BluetoothProfile.LE_AUDIO, receiver);
                     } else {
@@ -4977,7 +5010,7 @@
     }
 
     private void groupMutexLock(boolean isReadOnly) {
-        if (mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+        if (Flags.leaudioApiSynchronizedBlockFix()) {
             if (isReadOnly) {
                 mGroupReadLock.lock();
             } else {
@@ -4989,7 +5022,7 @@
     }
 
     private void groupMutexUnlock(boolean isReadOnly) {
-        if (mFeatureFlags.leaudioApiSynchronizedBlockFix()) {
+        if (Flags.leaudioApiSynchronizedBlockFix()) {
             if (isReadOnly) {
                 mGroupReadLock.unlock();
             } else {
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java
index d7939a3..f9a240d 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java
@@ -53,7 +53,7 @@
 import android.util.Log;
 
 import com.android.bluetooth.btservice.ProfileService;
-import com.android.bluetooth.flags.FeatureFlags;
+import com.android.bluetooth.flags.Flags;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -88,19 +88,16 @@
     private LeAudioNativeInterface mNativeInterface;
 
     private final BluetoothDevice mDevice;
-    private final FeatureFlags mFeatureFlags;
 
     LeAudioStateMachine(
             BluetoothDevice device,
             LeAudioService svc,
             LeAudioNativeInterface nativeInterface,
-            Looper looper,
-            FeatureFlags featureFlags) {
+            Looper looper) {
         super(TAG, looper);
         mDevice = device;
         mService = svc;
         mNativeInterface = nativeInterface;
-        mFeatureFlags = featureFlags;
 
         mDisconnected = new Disconnected();
         mConnecting = new Connecting();
@@ -119,11 +116,10 @@
             BluetoothDevice device,
             LeAudioService svc,
             LeAudioNativeInterface nativeInterface,
-            Looper looper,
-            FeatureFlags featureFlags) {
+            Looper looper) {
         Log.i(TAG, "make for device");
         LeAudioStateMachine LeAudioSm =
-                new LeAudioStateMachine(device, svc, nativeInterface, looper, featureFlags);
+                new LeAudioStateMachine(device, svc, nativeInterface, looper);
         LeAudioSm.start();
         return LeAudioSm;
     }
@@ -151,7 +147,7 @@
                 // Don't broadcast during startup
                 broadcastConnectionState(BluetoothProfile.STATE_DISCONNECTED,
                         mLastConnectionState);
-                if (mFeatureFlags.audioRoutingCentralization()) {
+                if (Flags.audioRoutingCentralization()) {
                     mService.deviceDisconnected(mDevice, false);
                 }
             }
@@ -441,7 +437,7 @@
                     + messageWhatToString(getCurrentMessage().what));
             mConnectionState = BluetoothProfile.STATE_CONNECTED;
             removeDeferredMessages(CONNECT);
-            if (mFeatureFlags.audioRoutingCentralization()) {
+            if (Flags.audioRoutingCentralization()) {
                 mService.deviceConnected(mDevice);
             }
             broadcastConnectionState(BluetoothProfile.STATE_CONNECTED, mLastConnectionState);
diff --git a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java
index 5e9d792..36c46bc 100644
--- a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java
+++ b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java
@@ -608,7 +608,6 @@
                                 ? op.mDescriptor.getCharacteristic().getUuid()
                                 : null));
         mEventLogger.logd(
-                DBG,
                 TAG,
                 "onAuthorizedGattOperation: device= "
                         + device
@@ -800,7 +799,6 @@
         for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
             List<ParcelUuid> uuidList = mMcpService.getNotificationSubscriptions(mCcid, device);
             mEventLogger.logd(
-                    DBG,
                     TAG,
                     "restoreCccValuesForStoredDevices: device= "
                             + device
@@ -810,7 +808,6 @@
             /* Restore CCCD values for device */
             for (ParcelUuid uuid : uuidList) {
                 mEventLogger.logd(
-                        DBG,
                         TAG,
                         "restoreCccValuesForStoredDevices: device= " + device + ", char= " + uuid);
                 setCcc(device, uuid.getUuid(), 0,
@@ -1026,7 +1023,7 @@
     }
 
     private void setInitialCharacteristicValues(boolean notify) {
-        mEventLogger.logd(DBG, TAG, "setInitialCharacteristicValues");
+        mEventLogger.logd(TAG, "setInitialCharacteristicValues");
         updateMediaStateChar(mCurrentMediaState.getValue());
         updatePlayerNameChar("", notify);
         updatePlayerIconUrlChar("");
@@ -1216,7 +1213,6 @@
 
         Request req = new Request(opcode, intVal);
         mEventLogger.logd(
-                DBG,
                 TAG,
                 "handleMediaControlPointRequest: sending "
                         + Request.Opcodes.toString(opcode)
@@ -1262,7 +1258,7 @@
     }
 
     private boolean initGattService(UUID serviceUuid) {
-        mEventLogger.logd(DBG, TAG, "initGattService: uuid= " + serviceUuid);
+        mEventLogger.logd(TAG, "initGattService: uuid= " + serviceUuid);
 
         if (mBluetoothGattServer == null) {
             BluetoothManager manager = mContext.getSystemService(BluetoothManager.class);
@@ -1334,10 +1330,10 @@
         }
 
         if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
-            mEventLogger.logd(DBG, TAG, "setCcc: device= " + device + ", notify: " + true);
+            mEventLogger.logd(TAG, "setCcc: device= " + device + ", notify: " + true);
             mMcpService.setNotificationSubscription(mCcid, device, new ParcelUuid(charUuid), true);
         } else if (Arrays.equals(value, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
-            mEventLogger.logd(DBG, TAG, "setCcc: device= " + device + ", notify: " + false);
+            mEventLogger.logd(TAG, "setCcc: device= " + device + ", notify: " + false);
             mMcpService.setNotificationSubscription(mCcid, device, new ParcelUuid(charUuid), false);
         } else {
             mEventLogger.loge(TAG, "Not handled CCC value: " + Arrays.toString(value));
@@ -1391,7 +1387,7 @@
 
         if (!isFeatureSupported(ServiceFeature.MEDIA_STATE)) return;
 
-        mEventLogger.logd(DBG, TAG, "updateMediaStateChar: state= " + MediaState.toString(state));
+        mEventLogger.logd(TAG, "updateMediaStateChar: state= " + MediaState.toString(state));
 
         BluetoothGattCharacteristic stateChar =
                 mCharacteristics.get(CharId.MEDIA_STATE);
@@ -1408,7 +1404,6 @@
         if (!isFeatureSupported(feature)) return;
 
         mEventLogger.logd(
-                DBG,
                 TAG,
                 "updateObjectIdChar: charId= "
                         + CharId.FromFeature(feature)
@@ -1625,7 +1620,6 @@
     public void onDeviceAuthorizationSet(BluetoothDevice device) {
         int auth = getDeviceAuthorization(device);
         mEventLogger.logd(
-                DBG,
                 TAG,
                 "onDeviceAuthorizationSet: device= "
                         + device
@@ -1684,7 +1678,7 @@
             if (notify && isFeatureSupported(ServiceFeature.PLAYING_ORDER_NOTIFY)) {
                 notifyCharacteristic(orderChar, null);
             }
-            mEventLogger.logd(DBG, TAG, "updatePlayingOrderChar: order= " + order);
+            mEventLogger.logd(TAG, "updatePlayingOrderChar: order= " + order);
         }
     }
 
@@ -1774,7 +1768,6 @@
                     notifyCharacteristic(characteristic, null);
                 }
                 mEventLogger.logd(
-                        DBG,
                         TAG,
                         "updateSeekingSpeedChar: intSpeed=" + intSpeed + ", speed= " + speed);
             }
@@ -1815,7 +1808,6 @@
                 notifyCharacteristic(characteristic, null);
             }
             mEventLogger.logd(
-                    DBG,
                     TAG,
                     "updatePlaybackSpeedChar: intSpeed=" + intSpeed + ", speed= " + speed);
         }
@@ -1847,7 +1839,6 @@
             }
         }
         mEventLogger.logd(
-                DBG,
                 TAG,
                 "updateTrackPositionChar: positionMs= " + positionMs + ", position= " + position);
     }
@@ -1883,7 +1874,6 @@
                 notifyCharacteristic(characteristic, null);
             }
             mEventLogger.logd(
-                    DBG,
                     TAG,
                     "updateTrackDurationChar: durationMs= "
                             + durationMs
@@ -1916,7 +1906,7 @@
             if (notify && isFeatureSupported(ServiceFeature.TRACK_TITLE_NOTIFY)) {
                 notifyCharacteristic(characteristic, null);
             }
-            mEventLogger.logd(DBG, TAG, "updateTrackTitleChar: title= '" + title + "'");
+            mEventLogger.logd(TAG, "updateTrackTitleChar: title= '" + title + "'");
         }
     }
 
@@ -1945,7 +1935,6 @@
             notifyCharacteristic(characteristic, null);
         }
         mEventLogger.logd(
-                DBG,
                 TAG,
                 "updateSupportedOpcodesChar: opcodes= "
                         + Request.SupportedOpcodes.toString(opcodes));
@@ -1959,15 +1948,13 @@
         if (isFeatureSupported(ServiceFeature.PLAYING_ORDER_SUPPORTED)) {
             mCharacteristics.get(CharId.PLAYING_ORDER_SUPPORTED)
                     .setValue(supportedOrder, BluetoothGattCharacteristic.FORMAT_UINT16, 0);
-            mEventLogger.logd(
-                    DBG, TAG, "updatePlayingOrderSupportedChar: order= " + supportedOrder);
+            mEventLogger.logd(TAG, "updatePlayingOrderSupportedChar: order= " + supportedOrder);
         }
     }
 
     private void updateIconObjIdChar(Long objId) {
         if (isFeatureSupported(ServiceFeature.PLAYER_ICON_OBJ_ID)) {
             mEventLogger.logd(
-                    DBG,
                     TAG,
                     "updateObjectIdChar charId= "
                             + CharId.PLAYER_ICON_OBJ_ID
@@ -2017,7 +2004,7 @@
         }
         if (isFeatureSupported(ServiceFeature.PLAYER_ICON_URL)) {
             mCharacteristics.get(CharId.PLAYER_ICON_URL).setValue(url);
-            mEventLogger.logd(DBG, TAG, "updatePlayerIconUrlChar: " + url);
+            mEventLogger.logd(TAG, "updatePlayerIconUrlChar: " + url);
         }
     }
 
@@ -2042,7 +2029,7 @@
         BluetoothGattCharacteristic characteristic =
                 mCharacteristics.get(CharId.PLAYER_NAME);
         characteristic.setValue(name);
-        mEventLogger.logd(DBG, TAG, "updatePlayerNameChar: name= '" + name + "'");
+        mEventLogger.logd(TAG, "updatePlayerNameChar: name= '" + name + "'");
         if (notify && isFeatureSupported(ServiceFeature.PLAYER_NAME_NOTIFY)) {
             notifyCharacteristic(characteristic, null);
         }
diff --git a/android/app/src/com/android/bluetooth/mcp/MediaControlProfile.java b/android/app/src/com/android/bluetooth/mcp/MediaControlProfile.java
index c88b72c..6c7219e 100644
--- a/android/app/src/com/android/bluetooth/mcp/MediaControlProfile.java
+++ b/android/app/src/com/android/bluetooth/mcp/MediaControlProfile.java
@@ -114,7 +114,6 @@
 
             if (metadata || state || queue) {
                 mEventLogger.logd(
-                        DBG,
                         TAG,
                         "onMediaUpdated: track_changed="
                                 + metadata
@@ -134,7 +133,6 @@
         @Override
         public void run(boolean availablePlayers, boolean addressedPlayers, boolean uids) {
             mEventLogger.logd(
-                    DBG,
                     TAG,
                     "onFolderUpdated: available_players= "
                             + availablePlayers
@@ -200,7 +198,6 @@
                 }
 
                 mEventLogger.logd(
-                        DBG,
                         TAG,
                         "onCurrentPlayerStateUpdated state= "
                                 + playback_state
@@ -220,7 +217,6 @@
         if (metadataChanged) {
             if (mCurrentData.metadata != null) {
                 mEventLogger.logd(
-                        DBG,
                         TAG,
                         "onCurrentPlayerStateUpdated metadata: title= '"
                                 + mCurrentData.metadata.title
@@ -288,13 +284,13 @@
     @Override
     public void onServiceInstanceRegistered(ServiceStatus status,
             MediaControlGattServiceInterface service) {
-        mEventLogger.logd(DBG, TAG, "onServiceInstanceRegistered: status= " + status);
+        mEventLogger.logd(TAG, "onServiceInstanceRegistered: status= " + status);
         mGMcsService = service;
     }
 
     @Override
     public void onServiceInstanceUnregistered(ServiceStatus status) {
-        mEventLogger.logd(DBG, TAG, "onServiceInstanceUnregistered: status= " + status);
+        mEventLogger.logd(TAG, "onServiceInstanceUnregistered: status= " + status);
         mGMcsService = null;
     }
 
@@ -345,13 +341,13 @@
 
     @Override
     public long onGetCurrentTrackPosition() {
-        mEventLogger.logd(DBG, TAG, "getCurrentTrackPosition");
+        mEventLogger.logd(TAG, "getCurrentTrackPosition");
         return getLatestTrackPosition();
     }
 
     @Override
     public void onTrackPositionSetRequest(long position) {
-        mEventLogger.logd(DBG, TAG, "GMCS onTrackPositionSetRequest");
+        mEventLogger.logd(TAG, "GMCS onTrackPositionSetRequest");
 
         if (mMediaPlayerList.getActivePlayer() == null) return;
         if ((getCurrentPlayerSupportedActions() & PlaybackState.ACTION_SEEK_TO) != 0) {
@@ -369,40 +365,39 @@
 
     @Override
     public void onCurrentTrackMetadataRequest() {
-        mEventLogger.logd(DBG, TAG, "GMCS onCurrentTrackMetadataRequest");
+        mEventLogger.logd(TAG, "GMCS onCurrentTrackMetadataRequest");
         // FIXME: Seems to be not used right now
     }
 
     @Override
     public void onPlayingOrderSetRequest(int order) {
-        mEventLogger.logd(DBG, TAG, "GMCS onPlayingOrderSetRequest");
+        mEventLogger.logd(TAG, "GMCS onPlayingOrderSetRequest");
         // Notice: MediaPlayerWrapper does not support play order control.
         // Ignore the request for now.
     }
 
     @Override
     public void onPlaybackSpeedSetRequest(float speed) {
-        mEventLogger.logd(DBG, TAG, "GMCS onPlaybackSpeedSetRequest");
+        mEventLogger.logd(TAG, "GMCS onPlaybackSpeedSetRequest");
         if (mMediaPlayerList.getActivePlayer() == null) return;
         mMediaPlayerList.getActivePlayer().setPlaybackSpeed(speed);
     }
 
     @Override
     public void onSetObjectIdRequest(int objField, long objectId) {
-        mEventLogger.logd(DBG, TAG, "GMCS onSetObjectIdRequest");
+        mEventLogger.logd(TAG, "GMCS onSetObjectIdRequest");
         // TODO: Implement once we have the Object Transfer Service
     }
 
     @Override
     public void onSearchRequest(SearchRequest request) {
-        mEventLogger.logd(DBG, TAG, "GMCS onSearchRequest");
+        mEventLogger.logd(TAG, "GMCS onSearchRequest");
         // TODO: Implement once we have the Object Transfer Service
     }
 
     @Override
     public void onMediaControlRequest(Request request) {
         mEventLogger.logd(
-                DBG,
                 TAG,
                 "GMCS onMediaControlRequest: opcode= "
                         + Request.Opcodes.toString(request.getOpcode()));
@@ -655,7 +650,6 @@
                                             getCurrentPlayerSupportedActions());
                             handled_request_map.put(settings_field, opcodes);
                             mEventLogger.logd(
-                                    DBG,
                                     TAG,
                                     "updateSupportedOpcodes setting supported opcodes to: "
                                             + opcodes);
@@ -763,7 +757,6 @@
             boolean isGenericMcs = appToken.equals(mContext.getPackageName());
 
             mEventLogger.logd(
-                    DBG,
                     TAG,
                     "Register MediaControlGattService instance ccid= "
                             + ccid
@@ -839,7 +832,7 @@
     private final Map<String, MediaControlGattServiceInterface> mServiceMap;
 
     public void unregisterServiceInstance(String appToken) {
-        mEventLogger.logd(DBG, TAG, "unregisterServiceInstance");
+        mEventLogger.logd(TAG, "unregisterServiceInstance");
 
         synchronized (mServiceMap) {
             MediaControlGattServiceInterface service = mServiceMap.get(appToken);
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
index 50b5f64..9ecefaf 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
@@ -1197,7 +1197,7 @@
 
         UUID charUuid = (op.mCharacteristic != null ? op.mCharacteristic.getUuid()
                 : (op.mDescriptor != null ? op.mDescriptor.getCharacteristic().getUuid() : null));
-        mEventLogger.logd(DBG, TAG, "onAuthorizedGattOperation device: " + device
+        mEventLogger.logd(TAG, "onAuthorizedGattOperation device: " + device
                         + ", opcode= " + op.mOperation
                         + ", characteristic= "
                         + (charUuid != null ? tbsUuidToString(charUuid) : "UNKNOWN"));
@@ -1362,7 +1362,7 @@
      */
     public void onDeviceAuthorizationSet(BluetoothDevice device) {
         int auth = getDeviceAuthorization(device);
-        mEventLogger.logd(DBG, TAG, "onDeviceAuthorizationSet: device= " + device
+        mEventLogger.logd(TAG, "onDeviceAuthorizationSet: device= " + device
                 + ", authorization= " + (auth == BluetoothDevice.ACCESS_ALLOWED ? "ALLOWED"
                         : (auth == BluetoothDevice.ACCESS_REJECTED ? "REJECTED" : "UNKNOWN")));
         processPendingGattOperations(device);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
index a8acf74..cc51199 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
@@ -116,8 +116,8 @@
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
         // Stub system interface
-        when(mSystemInterface.getHeadsetPhoneState()).thenReturn(mPhoneState);
-        when(mSystemInterface.getAudioManager()).thenReturn(mAudioManager);
+        doReturn(mPhoneState).when(mSystemInterface).getHeadsetPhoneState();
+        doReturn(mAudioManager).when(mSystemInterface).getAudioManager();
         // This line must be called to make sure relevant objects are initialized properly
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         // Get a device for testing
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
index f1ec210..f95bd87 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
@@ -1356,9 +1356,9 @@
     @Test
     public void testProcessConnectMessage_onConnectingState() {
         initToConnectingState();
-        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT);
         assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
                 HeadsetClientStateMachine.CONNECT)).isFalse();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT);
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
         Assert.assertTrue(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
                 HeadsetClientStateMachine.CONNECT));
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java
index ad13645..d7f7d41 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java
@@ -35,6 +35,7 @@
 import android.content.AttributionSource;
 import android.os.ParcelUuid;
 import android.os.RemoteCallbackList;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -42,12 +43,12 @@
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.AudioRoutingManager;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
-import com.android.bluetooth.flags.FakeFeatureFlagsImpl;
 import com.android.bluetooth.flags.Flags;
 import com.android.bluetooth.x.com.android.modules.utils.SynchronousResultReceiver;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -58,8 +59,9 @@
 
 public class LeAudioBinderTest {
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private LeAudioService mLeAudioService;
-    private FakeFeatureFlagsImpl mFakeFlagsImpl;
     @Mock private AdapterService mAdapterService;
     @Mock private LeAudioNativeInterface mNativeInterface;
     @Mock private DatabaseManager mDatabaseManager;
@@ -83,15 +85,10 @@
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
         doReturn(mAudioRoutingManager).when(mAdapterService).getActiveDeviceManager();
 
-        mFakeFlagsImpl = new FakeFeatureFlagsImpl();
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_API_SYNCHRONIZED_BLOCK_FIX, false);
-
         mLeAudioService =
                 spy(
                         new LeAudioService(
-                                InstrumentationRegistry.getTargetContext(),
-                                mNativeInterface,
-                                mFakeFlagsImpl));
+                                InstrumentationRegistry.getTargetContext(), mNativeInterface));
         mLeAudioService.start();
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mBinder = new LeAudioService.BluetoothLeAudioBinder(mLeAudioService);
@@ -173,12 +170,12 @@
         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
         AttributionSource source = new AttributionSource.Builder(0).build();
 
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, false);
+        mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
         SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
         mBinder.setActiveDevice(device, source, recv);
         verify(mLeAudioService).setActiveDevice(device);
 
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
         recv = SynchronousResultReceiver.get();
         mBinder.setActiveDevice(device, source, recv);
         verify(mAudioRoutingManager).activateDeviceProfile(device, BluetoothProfile.LE_AUDIO, recv);
@@ -188,12 +185,12 @@
     public void setActiveDevice_withNullDevice_callsRemoveActiveDevice() {
         AttributionSource source = new AttributionSource.Builder(0).build();
 
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, false);
+        mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
         SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
         mBinder.setActiveDevice(null, source, recv);
         verify(mLeAudioService).removeActiveDevice(true);
 
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
         recv = SynchronousResultReceiver.get();
         mBinder.setActiveDevice(null, source, recv);
         verify(mAudioRoutingManager).activateDeviceProfile(null, BluetoothProfile.LE_AUDIO, recv);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
index 81d19db..8525f72 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
@@ -30,21 +30,24 @@
 import android.media.AudioManager;
 import android.os.Looper;
 import android.os.ParcelUuid;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 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;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
-import com.android.bluetooth.flags.FakeFeatureFlagsImpl;
 import com.android.bluetooth.flags.Flags;
 
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -59,6 +62,9 @@
 @RunWith(AndroidJUnit4.class)
 public class LeAudioBroadcastServiceTest {
     private static final int TIMEOUT_MS = 1000;
+
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private BluetoothAdapter mAdapter;
     private BluetoothDevice mDevice;
     private Context mTargetContext;
@@ -75,7 +81,9 @@
     @Mock private LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface;
     @Mock private LeAudioNativeInterface mLeAudioNativeInterface;
     @Mock private LeAudioTmapGattServer mTmapGattServer;
+    @Mock private BassClientService mBassClientService;
     @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
+    @Spy private ServiceFactory mServiceFactory = new ServiceFactory();
 
     private static final String TEST_MAC_ADDRESS = "00:11:22:33:44:55";
     private static final int TEST_BROADCAST_ID = 42;
@@ -96,6 +104,27 @@
     private static final String TEST_LANGUAGE = "deu";
     private static final String TEST_BROADCAST_NAME = "Name Test";
 
+    private static final BluetoothLeAudioCodecConfig LC3_16KHZ_CONFIG =
+            new BluetoothLeAudioCodecConfig.Builder()
+                    .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
+                    .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000)
+                    .build();
+    private static final BluetoothLeAudioCodecConfig LC3_48KHZ_CONFIG =
+            new BluetoothLeAudioCodecConfig.Builder()
+                    .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
+                    .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000)
+                    .build();
+
+    private static final List<BluetoothLeAudioCodecConfig> INPUT_SELECTABLE_CONFIG_STANDARD =
+            List.of(LC3_16KHZ_CONFIG);
+    private static final List<BluetoothLeAudioCodecConfig> OUTPUT_SELECTABLE_CONFIG_STANDARD =
+            List.of(LC3_16KHZ_CONFIG);
+
+    private static final List<BluetoothLeAudioCodecConfig> INPUT_SELECTABLE_CONFIG_HIGH =
+            List.of(LC3_48KHZ_CONFIG);
+    private static final List<BluetoothLeAudioCodecConfig> OUTPUT_SELECTABLE_CONFIG_HIGH =
+            List.of(LC3_48KHZ_CONFIG);
+
     private boolean mOnBroadcastStartedCalled = false;
     private boolean mOnBroadcastStartFailedCalled = false;
     private boolean mOnBroadcastStoppedCalled = false;
@@ -106,7 +135,6 @@
     private boolean mOnBroadcastUpdateFailedCalled = false;
     private boolean mOnBroadcastMetadataChangedCalled = false;
     private int mOnBroadcastStartFailedReason = BluetoothStatusCodes.SUCCESS;
-    private FakeFeatureFlagsImpl mFakeFlagsImpl;
 
     private final IBluetoothLeBroadcastCallback mCallbacks =
             new IBluetoothLeBroadcastCallback.Stub() {
@@ -188,15 +216,9 @@
         LeAudioNativeInterface.setInstance(mLeAudioNativeInterface);
         startService();
 
-        mFakeFlagsImpl = new FakeFeatureFlagsImpl();
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT, false);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, false);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, false);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_API_SYNCHRONIZED_BLOCK_FIX, false);
-        mService.setFeatureFlags(mFakeFlagsImpl);
-
         mService.mAudioManager = mAudioManager;
-
+        mService.mServiceFactory = mServiceFactory;
+        when(mServiceFactory.getBassClientService()).thenReturn(mBassClientService);
         // Set up the State Changed receiver
         IntentFilter filter = new IntentFilter();
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -213,14 +235,16 @@
 
     @After
     public void tearDown() throws Exception {
-        if (mService == null) {
+        if (mService == null || mAdapter == null) {
             return;
         }
+        if (mLeAudioIntentReceiver != null) {
+            mTargetContext.unregisterReceiver(mLeAudioIntentReceiver);
+        }
 
         stopService();
         LeAudioBroadcasterNativeInterface.setInstance(null);
         LeAudioNativeInterface.setInstance(null);
-        mTargetContext.unregisterReceiver(mLeAudioIntentReceiver);
         TestUtils.clearAdapterService(mAdapterService);
         reset(mAudioManager);
     }
@@ -399,6 +423,44 @@
     }
 
     @Test
+    public void testCreateBroadcast_updateQualityToStandard() {
+        byte[] code = {0x00, 0x01, 0x00, 0x02};
+        int groupId = 1;
+        prepareConnectedUnicastDevice(groupId);
+
+        mService.mBroadcastCallbacks.register(mCallbacks);
+
+        BluetoothLeAudioContentMetadata.Builder meta_builder =
+                new BluetoothLeAudioContentMetadata.Builder();
+        BluetoothLeAudioContentMetadata meta = meta_builder.build();
+        BluetoothLeBroadcastSettings settings = buildBroadcastSettingsFromMetadata(meta, code, 1);
+
+        when(mBassClientService.getConnectedDevices()).thenReturn(List.of(mDevice));
+        // update selectable configs to be STANDARD quality
+        injectGroupSelectableCodecConfigChanged(
+                groupId, INPUT_SELECTABLE_CONFIG_STANDARD, OUTPUT_SELECTABLE_CONFIG_STANDARD);
+        injectGroupCurrentCodecConfigChanged(groupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG);
+
+        mService.createBroadcast(settings);
+
+        // Test data with only one subgroup
+        // Verify quality is updated to standard per sinks capabilities
+        int[] expectedQualityArray = {BluetoothLeBroadcastSubgroupSettings.QUALITY_STANDARD};
+        byte[][] expectedDataArray = {
+            settings.getSubgroupSettings().get(0).getContentMetadata().getRawMetadata()
+        };
+
+        verify(mLeAudioBroadcasterNativeInterface, times(1))
+                .createBroadcast(
+                        eq(true),
+                        eq(TEST_BROADCAST_NAME),
+                        eq(code),
+                        eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
+                        eq(expectedQualityArray),
+                        eq(expectedDataArray));
+    }
+
+    @Test
     public void testStartStopBroadcastNative() {
         int broadcastId = 243;
         byte[] code = {0x00, 0x01, 0x00, 0x02};
@@ -640,6 +702,11 @@
         create_event.valueInt4 = srcAudioLocation;
         create_event.valueInt5 = availableContexts;
         mService.messageFromNative(create_event);
+
+        // Set default codec config to HIGH quality
+        injectGroupSelectableCodecConfigChanged(
+                groupId, INPUT_SELECTABLE_CONFIG_HIGH, OUTPUT_SELECTABLE_CONFIG_HIGH);
+        injectGroupCurrentCodecConfigChanged(groupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG);
     }
 
     @Test
@@ -647,7 +714,7 @@
         int groupId = 1;
         byte[] code = {0x00, 0x01, 0x00, 0x02};
 
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
 
         prepareConnectedUnicastDevice(groupId);
 
@@ -733,8 +800,8 @@
         int broadcastId = 243;
         byte[] code = {0x00, 0x01, 0x00, 0x02};
 
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
+        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES);
 
         mService.mBroadcastCallbacks.register(mCallbacks);
 
@@ -846,8 +913,8 @@
         int broadcastId = 243;
         byte[] code = {0x00, 0x01, 0x00, 0x02};
 
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
+        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES);
 
         mService.mBroadcastCallbacks.register(mCallbacks);
 
@@ -983,7 +1050,8 @@
 
         BluetoothLeBroadcastSubgroupSettings.Builder subgroupBuilder =
                 new BluetoothLeBroadcastSubgroupSettings.Builder()
-                .setContentMetadata(contentMetadata);
+                        .setContentMetadata(contentMetadata)
+                        .setPreferredQuality(BluetoothLeBroadcastSubgroupSettings.QUALITY_HIGH);
 
         BluetoothLeBroadcastSettings.Builder builder = new BluetoothLeBroadcastSettings.Builder()
                         .setPublicBroadcast(true)
@@ -997,4 +1065,30 @@
         }
         return builder.build();
     }
+
+    private void injectGroupCurrentCodecConfigChanged(
+            int groupId,
+            BluetoothLeAudioCodecConfig inputCodecConfig,
+            BluetoothLeAudioCodecConfig outputCodecConfig) {
+        int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED;
+
+        LeAudioStackEvent groupCodecConfigChangedEvent = new LeAudioStackEvent(eventType);
+        groupCodecConfigChangedEvent.valueInt1 = groupId;
+        groupCodecConfigChangedEvent.valueCodec1 = inputCodecConfig;
+        groupCodecConfigChangedEvent.valueCodec2 = outputCodecConfig;
+        mService.messageFromNative(groupCodecConfigChangedEvent);
+    }
+
+    private void injectGroupSelectableCodecConfigChanged(
+            int groupId,
+            List<BluetoothLeAudioCodecConfig> inputSelectableCodecConfig,
+            List<BluetoothLeAudioCodecConfig> outputSelectableCodecConfig) {
+        int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED;
+
+        LeAudioStackEvent groupCodecConfigChangedEvent = new LeAudioStackEvent(eventType);
+        groupCodecConfigChangedEvent.valueInt1 = groupId;
+        groupCodecConfigChangedEvent.valueCodecList1 = inputSelectableCodecConfig;
+        groupCodecConfigChangedEvent.valueCodecList2 = outputSelectableCodecConfig;
+        mService.messageFromNative(groupCodecConfigChangedEvent);
+    }
 }
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 022abb3..30bb834 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
@@ -51,6 +51,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
@@ -62,7 +63,6 @@
 import com.android.bluetooth.btservice.ServiceFactory;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.csip.CsipSetCoordinatorService;
-import com.android.bluetooth.flags.FakeFeatureFlagsImpl;
 import com.android.bluetooth.flags.Flags;
 import com.android.bluetooth.hap.HapClientService;
 import com.android.bluetooth.hfp.HeadsetService;
@@ -72,6 +72,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -97,6 +98,8 @@
     private static final int MAX_LE_AUDIO_CONNECTIONS = 5;
     private static final int LE_AUDIO_GROUP_ID_INVALID = -1;
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private BluetoothAdapter mAdapter;
     private Context mTargetContext;
     private LeAudioService mService;
@@ -112,7 +115,6 @@
     private boolean onGroupStreamStatusCallbackCalled = false;
     private boolean onGroupCodecConfChangedCallbackCalled = false;
     private BluetoothLeAudioCodecStatus testCodecStatus = null;
-    private FakeFeatureFlagsImpl mFakeFlagsImpl;
 
     private BroadcastReceiver mLeAudioIntentReceiver;
 
@@ -195,14 +197,6 @@
         LeAudioNativeInterface.setInstance(mNativeInterface);
         startService();
 
-        mFakeFlagsImpl = new FakeFeatureFlagsImpl();
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT, false);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, false);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, false);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_MCS_TBS_AUTHORIZATION_REBOND_FIX, false);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_API_SYNCHRONIZED_BLOCK_FIX, false);
-        mService.setFeatureFlags(mFakeFlagsImpl);
-
         mService.mAudioManager = mAudioManager;
         mService.mMcpService = mMcpService;
         mService.mTbsService = mTbsService;
@@ -811,8 +805,8 @@
     /** Test that authorization info is removed from TBS and MCS after the device is unbond. */
     @Test
     public void testAuthorizationInfoRemovedFromTbsMcsOnUnbondEvents() {
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_MCS_TBS_AUTHORIZATION_REBOND_FIX, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
+        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_MCS_TBS_AUTHORIZATION_REBOND_FIX);
 
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
@@ -942,7 +936,7 @@
 
     @Test
     public void testAuthorizationInfoRemovedFromTbsMcsOnUnbondEventsWithSynchBlockFixFlag() {
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_API_SYNCHRONIZED_BLOCK_FIX, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_API_SYNCHRONIZED_BLOCK_FIX);
 
         testAuthorizationInfoRemovedFromTbsMcsOnUnbondEvents();
     }
@@ -1737,8 +1731,8 @@
 
     @Test
     public void testMediaContextUnavailableForAWhile() {
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT, true);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT);
+        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
 
         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
         connectTestDevice(mSingleDevice, testGroupId);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java
index 73365d6..d372c17 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java
@@ -42,8 +42,6 @@
 
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
-import com.android.bluetooth.flags.FakeFeatureFlagsImpl;
-import com.android.bluetooth.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -60,7 +58,6 @@
     private HandlerThread mHandlerThread;
     private LeAudioStateMachine mLeAudioStateMachine;
     private BluetoothDevice mTestDevice;
-    private FakeFeatureFlagsImpl mFakeFlagsImpl;
     private static final int TIMEOUT_MS = 1000;
 
     @Mock private AdapterService mAdapterService;
@@ -75,9 +72,6 @@
         TestUtils.setAdapterService(mAdapterService);
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mFakeFlagsImpl = new FakeFeatureFlagsImpl();
-        mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, false);
-        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, false);
 
         // Get a device for testing
         mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
@@ -92,8 +86,7 @@
                         mTestDevice,
                         mLeAudioService,
                         mLeAudioNativeInterface,
-                        mHandlerThread.getLooper(),
-                        mFakeFlagsImpl);
+                        mHandlerThread.getLooper());
     }
 
     @After
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceCleanupTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceCleanupTest.java
new file mode 100644
index 0000000..04ab4ee
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceCleanupTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.opp;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.BluetoothMethodProxy;
+import com.android.bluetooth.TestUtils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothOppServiceCleanupTest {
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private boolean mIsAdapterServiceSet;
+
+    private Context mTargetContext;
+
+    @Mock private AdapterService mAdapterService;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        TestUtils.setAdapterService(mAdapterService);
+        mIsAdapterServiceSet = true;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        BluetoothMethodProxy.setInstanceForTesting(null);
+
+        if (mIsAdapterServiceSet) {
+            TestUtils.clearAdapterService(mAdapterService);
+        }
+    }
+
+    @Test
+    @UiThreadTest
+    public void testStopAndCleanup() {
+        mSetFlagsRule.enableFlags(
+                Flags.FLAG_OPP_SERVICE_FIX_INDEX_OUT_OF_BOUNDS_EXCEPTION_IN_UPDATE_THREAD);
+
+        // Don't need to disable again since it will be handled in OppService.stop
+        enableBtOppProvider();
+
+        // Add thousands of placeholder rows
+        for (int i = 0; i < 2000; i++) {
+            ContentValues values = new ContentValues();
+            mTargetContext.getContentResolver().insert(BluetoothShare.CONTENT_URI, values);
+        }
+
+        try {
+            BluetoothOppService service = new BluetoothOppService(mTargetContext);
+            service.start();
+            service.setAvailable(true);
+
+            // Call stop while UpdateThread is running.
+            service.stop();
+            service.cleanup();
+        } finally {
+            mTargetContext.getContentResolver().delete(BluetoothShare.CONTENT_URI, null, null);
+        }
+    }
+
+    private void enableBtOppProvider() {
+        mTargetContext
+                .getPackageManager()
+                .setApplicationEnabledSetting(
+                        mTargetContext.getPackageName(),
+                        COMPONENT_ENABLED_STATE_ENABLED,
+                        DONT_KILL_APP);
+
+        ComponentName activityName =
+                new ComponentName(mTargetContext, BluetoothOppProvider.class.getCanonicalName());
+        mTargetContext
+                .getPackageManager()
+                .setComponentEnabledSetting(
+                        activityName, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/TestActivity.java b/android/app/tests/unit/src/com/android/bluetooth/opp/TestActivity.java
index 65c945f..2710aa1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/TestActivity.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/TestActivity.java
@@ -359,9 +359,9 @@
 
     private static final String TAG = "BtOppRfcommListener";
 
-    private static final boolean D = Constants.DEBUG;
+    private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final boolean V = Constants.VERBOSE;
+    private static final boolean V = Log.isLoggable(TAG, Log.VERBOSE);
 
     private volatile boolean mInterrupted;
 
@@ -477,7 +477,7 @@
 class TestTcpServer extends ServerRequestHandler implements Runnable {
     private static final String TAG = "ServerRequestHandler";
 
-    private static final boolean V = Constants.VERBOSE;
+    private static final boolean V = Log.isLoggable(TAG, Log.VERBOSE);
 
     static final int PORT = 6500;
 
diff --git a/flags/pairing.aconfig b/flags/pairing.aconfig
new file mode 100644
index 0000000..8a9454f
--- /dev/null
+++ b/flags/pairing.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.bluetooth.flags"
+container: "com.android.btservices"
+
+flag {
+    name: "clear_collision_state_on_pairing_complete"
+    namespace: "bluetooth"
+    description: "Clear authentication collision state on pairing conclusion"
+    bug: "327208896"
+}
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java b/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java
index 00fbbe0..2d7db91 100644
--- a/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java
@@ -492,7 +492,7 @@
         private BluetoothDevice mSourceDevice = null;
         private int mSourceAdvertisingSid = UNKNOWN_VALUE_PLACEHOLDER;
         private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
-        private int mPaSyncInterval = UNKNOWN_VALUE_PLACEHOLDER;
+        private int mPaSyncInterval = PA_SYNC_INTERVAL_UNKNOWN;
         private boolean mIsEncrypted = false;
         private boolean mIsPublicBroadcast = false;
         private String mBroadcastName = null;
diff --git a/system/audio/Android.bp b/system/audio/Android.bp
index c0e3119..2fc5464 100644
--- a/system/audio/Android.bp
+++ b/system/audio/Android.bp
@@ -14,9 +14,6 @@
         "asrc/asrc_resampler.cc",
         "asrc/asrc_tables.cc",
     ],
-    include_dirs: [
-        "packages/modules/Bluetooth/system/gd",
-    ],
     shared_libs: [
         "libchrome",
     ],
@@ -44,7 +41,6 @@
     stl: "libc++_static",
     include_dirs: [
         "packages/modules/Bluetooth/system",
-        "packages/modules/Bluetooth/system/gd",
     ],
     generated_headers: [
         "BluetoothGeneratedDumpsysDataSchema_h",
diff --git a/system/audio/asrc/asrc_resampler.cc b/system/audio/asrc/asrc_resampler.cc
index 0590b84..09e5958 100644
--- a/system/audio/asrc/asrc_resampler.cc
+++ b/system/audio/asrc/asrc_resampler.cc
@@ -19,15 +19,15 @@
 #include <base/logging.h>
 #include <base/strings/stringprintf.h>
 
+#include <algorithm>
 #include <cmath>
 #include <utility>
 
 #include "asrc_tables.h"
-#include "hal/nocp_iso_clocker.h"
 
 namespace bluetooth::audio::asrc {
 
-class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler {
+class SourceAudioHalAsrc::ClockRecovery : public ClockHandler {
   const int interval_;
 
   std::mutex mutex_;
@@ -113,8 +113,7 @@
     }
 
     int dt_current = int(timestamp_us - link.local_time);
-    if (std::abs(dt_current) < std::abs(link.decim_dt[1]))
-      link.decim_dt[1] = dt_current;
+    link.decim_dt[1] = std::min(link.decim_dt[1], dt_current);
 
     if (link.local_time - link.decim_t0 < 1000 * 1000) return;
 
@@ -237,11 +236,9 @@
         state_{
             .link = {{.state = LinkState::RESET}, {.state = LinkState::RESET}},
             .active_link_id = -1},
-        reference_timing_{0, 0, 0} {
-    ::bluetooth::hal::NocpIsoClocker::Register(this);
-  }
+        reference_timing_{0, 0, 0} {}
 
-  ~ClockRecovery() override { ::bluetooth::hal::NocpIsoClocker::Unregister(); }
+  ~ClockRecovery() override {}
 
   __attribute__((no_sanitize("integer"))) uint32_t Convert(
       uint32_t stream_time) {
@@ -466,16 +463,16 @@
 
 #endif
 
-SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate,
-                                       int bit_depth, int interval_us,
-                                       int num_burst_buffers,
-                                       int burst_delay_ms)
+SourceAudioHalAsrc::SourceAudioHalAsrc(
+    std::shared_ptr<ClockSource> clock_source, int channels, int sample_rate,
+    int bit_depth, int interval_us, int num_burst_buffers, int burst_delay_ms)
     : sample_rate_(sample_rate),
       bit_depth_(bit_depth),
       interval_us_(interval_us),
       stream_us_(0),
       drift_us_(0),
       out_counter_(0),
+      clock_source_(std::move(clock_source)),
       resampler_pos_{0, 0} {
   buffers_size_ = 0;
 
@@ -509,6 +506,7 @@
   // when the PCM bit_depth is higher than 16 bits.
 
   clock_recovery_ = std::make_unique<ClockRecovery>(interval_us_);
+  clock_source_->Bind(clock_recovery_.get());
   resamplers_ = std::make_unique<std::vector<Resampler>>(channels, bit_depth_);
 
   // Deduct from the PCM stream characteristics, the size of the pool buffers
diff --git a/system/audio/asrc/asrc_resampler.h b/system/audio/asrc/asrc_resampler.h
index ae9ea31..400c51c 100644
--- a/system/audio/asrc/asrc_resampler.h
+++ b/system/audio/asrc/asrc_resampler.h
@@ -23,6 +23,19 @@
 
 namespace bluetooth::audio::asrc {
 
+class ClockHandler {
+ public:
+  virtual ~ClockHandler() = default;
+  virtual void OnEvent(uint32_t timestamp, int link_id,
+                       int num_of_completed_packets) = 0;
+};
+
+class ClockSource {
+ public:
+  virtual ~ClockSource() = default;
+  virtual void Bind(ClockHandler*) = 0;
+};
+
 class SourceAudioHalAsrc {
  public:
   // The Asynchronous Sample Rate Conversion (ASRC) is set up from the PCM
@@ -36,9 +49,9 @@
   // `burst_delay_ms` helps to ensure that the synchronization with the
   // transmission intervals is done.
 
-  SourceAudioHalAsrc(int channels, int sample_rate, int bit_depth,
-                     int interval_us, int num_burst_buffers = 2,
-                     int burst_delay_ms = 500);
+  SourceAudioHalAsrc(std::shared_ptr<ClockSource> clock_source, int channels,
+                     int sample_rate, int bit_depth, int interval_us,
+                     int num_burst_buffers = 2, int burst_delay_ms = 500);
 
   ~SourceAudioHalAsrc();
 
@@ -74,6 +87,7 @@
 
   class ClockRecovery;
   std::unique_ptr<ClockRecovery> clock_recovery_;
+  std::shared_ptr<ClockSource> clock_source_;
 
   class Resampler;
   std::unique_ptr<std::vector<Resampler>> resamplers_;
diff --git a/system/audio/asrc/asrc_resampler_test.cc b/system/audio/asrc/asrc_resampler_test.cc
index 04bf07e..1b90e0a 100644
--- a/system/audio/asrc/asrc_resampler_test.cc
+++ b/system/audio/asrc/asrc_resampler_test.cc
@@ -19,17 +19,17 @@
 #include <cstdio>
 #include <iostream>
 
-namespace bluetooth::hal {
-void NocpIsoClocker::Register(NocpIsoHandler*) {}
-void NocpIsoClocker::Unregister() {}
-}  // namespace bluetooth::hal
-
 namespace bluetooth::audio::asrc {
 
+class MockClockSource : public ClockSource {
+  void Bind(ClockHandler*) override {}
+};
+
 class SourceAudioHalAsrcTest : public SourceAudioHalAsrc {
  public:
   SourceAudioHalAsrcTest(int channels, int bitdepth)
-      : SourceAudioHalAsrc(channels, 48000, bitdepth, 10000) {}
+      : SourceAudioHalAsrc(std::make_unique<MockClockSource>(), channels, 48000,
+                           bitdepth, 10000) {}
 
   template <typename T>
   void Resample(double ratio, const T* in, size_t in_length, size_t* in_count,
diff --git a/system/audio_hal_interface/Android.bp b/system/audio_hal_interface/Android.bp
index ffa646d..fa629d3 100644
--- a/system/audio_hal_interface/Android.bp
+++ b/system/audio_hal_interface/Android.bp
@@ -88,7 +88,9 @@
     name: "bluetooth-test-audio-hal-interface",
     defaults: [
         "fluoride_defaults",
-        "latest_android_hardware_bluetooth_audio_ndk_shared",
+        "latest_android_hardware_audio_common_ndk_static",
+        "latest_android_hardware_bluetooth_audio_ndk_static",
+        "latest_android_media_audio_common_types_ndk_static",
     ],
     include_dirs: [
         "packages/modules/Bluetooth/system",
@@ -99,8 +101,6 @@
         "hidl/client_interface_hidl_unittest.cc",
     ],
     shared_libs: [
-        "android.hardware.bluetooth.audio@2.0",
-        "android.hardware.bluetooth.audio@2.1",
         "libbase",
         "libbinder_ndk",
         "libcutils",
@@ -110,6 +110,11 @@
         "libutils",
     ],
     static_libs: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth.audio@2.1",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
         "libbluetooth_log",
         "libbt-audio-hal-interface",
         "libbt-common",
@@ -128,7 +133,9 @@
     test_suites: ["general-tests"],
     defaults: [
         "fluoride_defaults",
-        "latest_android_hardware_bluetooth_audio_ndk_shared",
+        "latest_android_hardware_audio_common_ndk_static",
+        "latest_android_hardware_bluetooth_audio_ndk_static",
+        "latest_android_media_audio_common_types_ndk_static",
         "mts_defaults",
     ],
     cflags: [
@@ -174,21 +181,28 @@
         "aidl/a2dp_provider_info_unittest.cc",
     ],
     shared_libs: [
-        "libbase",
-        "libbase",
+        "libbinder_ndk",
         "libcutils",
-        "libfmq",
+        "libhidlbase",
         "liblog",
         "libutils",
         "server_configurable_flags",
     ],
     static_libs: [
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth.audio@2.1",
+        "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.common.fmq-V1-ndk",
         "bluetooth_flags_c_lib",
+        "libbase",
         "libbluetooth_log",
         "libbt-common",
         "libbt_shim_bridge",
         "libchrome",
         "libflagtest",
+        "libfmq",
         "libgmock",
         "libosi",
     ],
diff --git a/system/audio_hal_interface/le_audio_software.cc b/system/audio_hal_interface/le_audio_software.cc
index 5793b44..9ec3b21 100644
--- a/system/audio_hal_interface/le_audio_software.cc
+++ b/system/audio_hal_interface/le_audio_software.cc
@@ -637,8 +637,6 @@
 }
 
 void LeAudioClientInterface::Source::ConfirmStreamingRequestV2() {
-  LOG_INFO("Rymek source");
-
   auto lambda = [&](StartRequestState currect_start_request_state)
       -> std::pair<StartRequestState, bool> {
     switch (currect_start_request_state) {
diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc
index a32d7b2..3b28bdf 100644
--- a/system/bta/hearing_aid/hearing_aid.cc
+++ b/system/bta/hearing_aid/hearing_aid.cc
@@ -32,12 +32,14 @@
 #include <mutex>
 #include <vector>
 
+#include "audio/asrc/asrc_resampler.h"
 #include "bta/include/bta_gatt_api.h"
 #include "bta/include/bta_gatt_queue.h"
 #include "bta/include/bta_hearing_aid_api.h"
 #include "btm_iso_api.h"
 #include "device/include/controller.h"
 #include "embdrv/g722/g722_enc_dec.h"
+#include "hal/link_clocker.h"
 #include "hardware/bt_gatt_types.h"
 #include "include/check.h"
 #include "internal_include/bt_trace.h"
@@ -275,6 +277,13 @@
   const int DROP_FREQUENCY_THRESHOLD =
       bluetooth::common::init_flags::get_asha_packet_drop_frequency_threshold();
 
+  // Resampler context for audio stream.
+  // Clock recovery uses L2CAP Flow Control Credit Ind acknowledgments
+  // from either the left or right connection, whichever is first
+  // connected.
+  std::shared_ptr<bluetooth::hal::L2capCreditIndEvents> asrc_clock_source;
+  std::unique_ptr<bluetooth::audio::asrc::SourceAudioHalAsrc> asrc;
+
  public:
   ~HearingAidImpl() override = default;
 
@@ -354,6 +363,51 @@
     }
   }
 
+  // Reset and configure the ASHA resampling context using the input device
+  // devices as reference for the BT clock estimation.
+  void ConfigureAsrc() {
+    if (!IS_FLAG_ENABLED(asha_asrc)) {
+      log::info("Asha resampling disabled: feature flag off");
+      return;
+    }
+
+    // Create a new ASRC context if required.
+    if (asrc == nullptr) {
+      asrc_clock_source =
+          std::make_shared<bluetooth::hal::L2capCreditIndEvents>();
+      asrc = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>(
+          asrc_clock_source, /*channels*/ 2,
+          /*sample_rate*/ codec_in_use == CODEC_G722_24KHZ ? 24000 : 16000,
+          /*bit_depth*/ 16,
+          /*interval_us*/ default_data_interval_ms * 1000,
+          /*num_burst_buffers*/ 0,
+          /*burst_delay*/ 0);
+    }
+
+    for (auto& device : hearingDevices.devices) {
+      if (!device.accepting_audio) {
+        continue;
+      }
+
+      uint16_t lcid = GAP_ConnGetL2CAPCid(device.gap_handle);
+      uint16_t rcid = 0;
+      L2CA_GetRemoteCid(lcid, &rcid);
+
+      auto conn = btm_acl_for_bda(device.address, BT_TRANSPORT_LE);
+      log::info("Updating ASRC context for handle=0x{:x}, cid=0x{:x}",
+                conn->Handle(), rcid);
+
+      asrc_clock_source->Update(device.isLeft(), conn->Handle(), rcid);
+    }
+  }
+
+  // Reset the ASHA resampling context.
+  void ResetAsrc() {
+    log::info("Resetting the Asha resampling context");
+    asrc_clock_source = nullptr;
+    asrc = nullptr;
+  }
+
   uint16_t UpdateBleConnParams(const RawAddress& address) {
     /* List of parameters that depends on the chosen Connection Interval */
     uint16_t min_ce_len = MIN_CE_LEN_20MS_CI;
@@ -1171,6 +1225,10 @@
     } else {
       log::info("audio_running={}", audio_running);
     }
+
+    // Close the ASRC context.
+    ResetAsrc();
+
     audio_running = false;
     stop_audio_ticks();
 
@@ -1213,6 +1271,9 @@
       return;
     }
 
+    // Open the ASRC context.
+    ConfigureAsrc();
+
     // TODO: shall we also reset the encoder ?
     encoder_state_release();
     encoder_state_init();
@@ -1352,6 +1413,16 @@
     return diff_credit < (init_credit / 2 - 1);
   }
 
+  void OnAudioDataReadyResample(const std::vector<uint8_t>& data) {
+    if (asrc == nullptr) {
+      return OnAudioDataReady(data);
+    }
+
+    for (auto const resampled_data : asrc->Run(data)) {
+      OnAudioDataReady(*resampled_data);
+    }
+  }
+
   void OnAudioDataReady(const std::vector<uint8_t>& data) {
     /* For now we assume data comes in as 16bit per sample 16kHz PCM stereo */
     bool need_drop = false;
@@ -1414,6 +1485,18 @@
       l2cap_flush_threshold = 1;
     }
 
+    // Skipping packets completely messes up the resampler context.
+    // The condition for skipping packets seems to be easily triggered,
+    // causing dropouts that could have been avoided.
+    //
+    // When the resampler is enabled, the flush threshold is set
+    // to the number of credits specified for the ASHA l2cap streaming
+    // channel. This will ensure it is only triggered in case of
+    // critical failure.
+    if (IS_FLAG_ENABLED(asha_asrc)) {
+      l2cap_flush_threshold = 8;
+    }
+
     // TODO: monural, binarual check
 
     // divide encoded data into packets, add header, send.
@@ -2069,7 +2152,7 @@
 class HearingAidAudioReceiverImpl : public HearingAidAudioReceiver {
  public:
   void OnAudioDataReady(const std::vector<uint8_t>& data) override {
-    if (instance) instance->OnAudioDataReady(data);
+    if (instance) instance->OnAudioDataReadyResample(data);
   }
   void OnAudioSuspend(const std::function<void()>& stop_audio_ticks) override {
     if (instance) instance->OnAudioSuspend(stop_audio_ticks);
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 f8c87c4..f2e8b12 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
@@ -27,6 +27,7 @@
 #include "bta/le_audio/codec_manager.h"
 #include "common/repeating_timer.h"
 #include "common/time_util.h"
+#include "gd/hal/link_clocker.h"
 #include "os/log.h"
 #include "osi/include/wakelock.h"
 #include "stack/include/main_thread.h"
@@ -248,6 +249,7 @@
   wakelock_acquire();
   if (IS_FLAG_ENABLED(leaudio_hal_client_asrc)) {
     asrc_ = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>(
+        std::make_shared<bluetooth::hal::NocpIsoEvents>(),
         source_codec_config_.num_channels, source_codec_config_.sample_rate,
         source_codec_config_.bits_per_sample,
         source_codec_config_.data_interval_us);
diff --git a/system/btcore/BUILD.gn b/system/btcore/BUILD.gn
index 5648584..f7b86f2 100644
--- a/system/btcore/BUILD.gn
+++ b/system/btcore/BUILD.gn
@@ -28,7 +28,10 @@
     "//bt/system",
   ]
 
-  configs += [ "//bt/system:target_defaults" ]
+  configs += [
+    "//bt/system:target_defaults",
+    "//bt/system/log:log_defaults",
+  ]
 
   deps = [
     "//bt/system/gd/rust/shim:init_flags_bridge_header",
@@ -57,6 +60,7 @@
     configs += [
       "//bt/system:external_gtest_main",
       "//bt/system:target_defaults",
+      "//bt/system/log:log_defaults",
     ]
 
     libs = [
diff --git a/system/btcore/src/module.cc b/system/btcore/src/module.cc
index c1d0617..b04434b 100644
--- a/system/btcore/src/module.cc
+++ b/system/btcore/src/module.cc
@@ -21,6 +21,7 @@
 #include "btcore/include/module.h"
 
 #include <base/logging.h>
+#include <bluetooth/log.h>
 #include <dlfcn.h>
 #include <string.h>
 
@@ -32,6 +33,7 @@
 #include "os/log.h"
 
 using bluetooth::common::MessageLoopThread;
+using namespace bluetooth;
 
 typedef enum {
   MODULE_STATE_NONE = 0,
@@ -63,7 +65,7 @@
   CHECK(get_module_state(module) == MODULE_STATE_NONE);
 
   if (!call_lifecycle_function(module->init)) {
-    LOG_ERROR("%s Failed to initialize module \"%s\"", __func__, module->name);
+    log::error("Failed to initialize module \"{}\"", module->name);
     return false;
   }
 
@@ -81,12 +83,12 @@
   CHECK(get_module_state(module) == MODULE_STATE_INITIALIZED ||
         module->init == NULL);
 
-  LOG_INFO("%s Starting module \"%s\"", __func__, module->name);
+  log::info("Starting module \"{}\"", module->name);
   if (!call_lifecycle_function(module->start_up)) {
-    LOG_ERROR("%s Failed to start up module \"%s\"", __func__, module->name);
+    log::error("Failed to start up module \"{}\"", module->name);
     return false;
   }
-  LOG_INFO("%s Started module \"%s\"", __func__, module->name);
+  log::info("Started module \"{}\"", module->name);
 
   set_module_state(module, MODULE_STATE_STARTED);
   return true;
@@ -100,12 +102,12 @@
   // Only something to do if the module was actually started
   if (state < MODULE_STATE_STARTED) return;
 
-  LOG_INFO("%s Shutting down module \"%s\"", __func__, module->name);
+  log::info("Shutting down module \"{}\"", module->name);
   if (!call_lifecycle_function(module->shut_down)) {
-    LOG_ERROR("%s Failed to shutdown module \"%s\". Continuing anyway.",
-              __func__, module->name);
+    log::error("Failed to shutdown module \"{}\". Continuing anyway.",
+               module->name);
   }
-  LOG_INFO("%s Shutdown of module \"%s\" completed", __func__, module->name);
+  log::info("Shutdown of module \"{}\" completed", module->name);
 
   set_module_state(module, MODULE_STATE_INITIALIZED);
 }
@@ -118,12 +120,12 @@
   // Only something to do if the module was actually initialized
   if (state < MODULE_STATE_INITIALIZED) return;
 
-  LOG_INFO("%s Cleaning up module \"%s\"", __func__, module->name);
+  log::info("Cleaning up module \"{}\"", module->name);
   if (!call_lifecycle_function(module->clean_up)) {
-    LOG_ERROR("%s Failed to cleanup module \"%s\". Continuing anyway.",
-              __func__, module->name);
+    log::error("Failed to cleanup module \"{}\". Continuing anyway.",
+               module->name);
   }
-  LOG_INFO("%s Cleanup of module \"%s\" completed", __func__, module->name);
+  log::info("Cleanup of module \"{}\" completed", module->name);
 
   set_module_state(module, MODULE_STATE_NONE);
 }
diff --git a/system/btif/co/bta_av_co.cc b/system/btif/co/bta_av_co.cc
index 58a9608..b96e3f3 100644
--- a/system/btif/co/bta_av_co.cc
+++ b/system/btif/co/bta_av_co.cc
@@ -24,6 +24,7 @@
  ******************************************************************************/
 
 #include <base/logging.h>
+#include <bluetooth/log.h>
 
 #include <mutex>
 #include <optional>
@@ -58,6 +59,8 @@
 #define BTA_AV_CO_AUDIO_INDEX_TO_HANDLE(index) \
   (((index) + 1) | BTA_AV_CHNL_AUDIO)
 
+using namespace bluetooth;
+
 class BtaAvCoSep {
  public:
   BtaAvCoSep()
@@ -776,7 +779,7 @@
 void BtaAvCo::Init(
     const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
     std::vector<btav_a2dp_codec_info_t>* supported_codecs) {
-  LOG_VERBOSE("%s", __func__);
+  log::verbose("");
 
   std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
@@ -824,7 +827,7 @@
   // hence we check only the first peer.
   A2dpCodecs* codecs = peers_[0].GetCodecs();
   if (codecs == nullptr) {
-    LOG_ERROR("Peer codecs is set to null");
+    log::error("Peer codecs is set to null");
     return false;
   }
   return codecs->isSupportedCodec(codec_index);
@@ -864,13 +867,12 @@
 
   index = BTA_AV_CO_AUDIO_HANDLE_TO_INDEX(bta_av_handle);
 
-  LOG_VERBOSE("%s: bta_av_handle = 0x%x index = %d", __func__, bta_av_handle,
-              index);
+  log::verbose("bta_av_handle = 0x{:x} index = {}", bta_av_handle, index);
 
   // Sanity check
   if (index >= BTA_AV_CO_NUM_ELEMENTS(peers_)) {
-    LOG_ERROR("%s: peer index %d for BTA AV handle 0x%x is out of bounds",
-              __func__, index, bta_av_handle);
+    log::error("peer index {} for BTA AV handle 0x{:x} is out of bounds", index,
+               bta_av_handle);
     return nullptr;
   }
 
@@ -879,19 +881,19 @@
 
 BtaAvCoPeer* BtaAvCo::FindPeerAndUpdate(tBTA_AV_HNDL bta_av_handle,
                                         const RawAddress& peer_address) {
-  LOG_VERBOSE("%s: peer %s bta_av_handle = 0x%x", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
+  log::verbose("peer {} bta_av_handle = 0x{:x}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
 
   BtaAvCoPeer* p_peer = FindPeer(bta_av_handle);
   if (p_peer == nullptr) {
-    LOG_ERROR("%s: peer entry for BTA AV handle 0x%x peer %s not found",
-              __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("peer entry for BTA AV handle 0x{:x} peer {} not found",
+               bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
     return nullptr;
   }
 
-  LOG_VERBOSE("%s: peer %s bta_av_handle = 0x%x previous address %s", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle,
-              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+  log::verbose("peer {} bta_av_handle = 0x{:x} previous address {}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle,
+               ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
   p_peer->addr = peer_address;
   return p_peer;
 }
@@ -908,23 +910,23 @@
                                      const RawAddress& peer_address,
                                      uint8_t num_seps, uint8_t num_sinks,
                                      uint8_t num_sources, uint16_t uuid_local) {
-  LOG_VERBOSE(
-      "%s: peer %s bta_av_handle:0x%x num_seps:%d num_sinks:%d num_sources:%d",
-      __func__, ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle, num_seps,
+  log::verbose(
+      "peer {} bta_av_handle:0x{:x} num_seps:{} num_sinks:{} num_sources:{}",
+      ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle, num_seps,
       num_sinks, num_sources);
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    LOG_ERROR("%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-              __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("could not find peer entry for bta_av_handle 0x{:x} peer {}",
+               bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
     return;
   }
 
   /* Sanity check : this should never happen */
   if (p_peer->opened) {
-    LOG_ERROR("%s: peer %s already opened", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("peer {} already opened",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address));
   }
 
   /* Copy the discovery results */
@@ -953,25 +955,24 @@
     tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address,
     uint8_t* p_codec_info, uint8_t* p_sep_info_idx, uint8_t seid,
     uint8_t* p_num_protect, uint8_t* p_protect_info) {
-  LOG_VERBOSE("%s: peer %s bta_av_handle:0x%x codec:%s seid:%d", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle,
-              A2DP_CodecName(p_codec_info), seid);
-  LOG_VERBOSE("%s: num_protect:0x%02x protect_info:0x%02x%02x%02x", __func__,
-              *p_num_protect, p_protect_info[0], p_protect_info[1],
-              p_protect_info[2]);
-  LOG_VERBOSE("%s: codec: %s", __func__,
-              A2DP_CodecInfoString(p_codec_info).c_str());
+  log::verbose("peer {} bta_av_handle:0x{:x} codec:{} seid:{}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle,
+               A2DP_CodecName(p_codec_info), seid);
+  log::verbose("num_protect:0x{:02x} protect_info:0x{:02x}{:02x}{:02x}",
+               *p_num_protect, p_protect_info[0], p_protect_info[1],
+               p_protect_info[2]);
+  log::verbose("codec: {}", A2DP_CodecInfoString(p_codec_info));
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    LOG_ERROR("%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-              __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("could not find peer entry for bta_av_handle 0x{:x} peer {}",
+               bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
     return A2DP_FAIL;
   }
-  LOG_VERBOSE("%s: peer(o=%d, n_sinks=%d, n_rx_sinks=%d, n_sup_sinks=%d)",
-              __func__, p_peer->opened, p_peer->num_sinks, p_peer->num_rx_sinks,
-              p_peer->num_sup_sinks);
+  log::verbose("peer(o={}, n_sinks={}, n_rx_sinks={}, n_sup_sinks={})",
+               p_peer->opened, p_peer->num_sinks, p_peer->num_rx_sinks,
+               p_peer->num_sup_sinks);
 
   p_peer->num_rx_sinks++;
 
@@ -989,9 +990,9 @@
     if (p_peer->num_sup_sinks < BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks)) {
       BtaAvCoSep* p_sink = &p_peer->sinks[p_peer->num_sup_sinks++];
 
-      LOG_VERBOSE("%s: saved caps[%x:%x:%x:%x:%x:%x]", __func__,
-                  p_codec_info[1], p_codec_info[2], p_codec_info[3],
-                  p_codec_info[4], p_codec_info[5], p_codec_info[6]);
+      log::verbose("saved caps[{:x}:{:x}:{:x}:{:x}:{:x}:{:x}]", p_codec_info[1],
+                   p_codec_info[2], p_codec_info[3], p_codec_info[4],
+                   p_codec_info[5], p_codec_info[6]);
 
       memcpy(p_sink->codec_caps, p_codec_info, AVDT_CODEC_SIZE);
       p_sink->sep_info_idx = *p_sep_info_idx;
@@ -999,8 +1000,8 @@
       p_sink->num_protect = *p_num_protect;
       memcpy(p_sink->protect_info, p_protect_info, AVDT_CP_INFO_LEN);
     } else {
-      LOG_ERROR("%s: peer %s : no more room for Sink info", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::error("peer {} : no more room for Sink info",
+                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
     }
   }
 
@@ -1010,9 +1011,9 @@
       (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
     return A2DP_FAIL;
   }
-  LOG_VERBOSE("%s: last Sink codec reached for peer %s (local %s)", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
-              p_peer->acceptor ? "acceptor" : "initiator");
+  log::verbose("last Sink codec reached for peer {} (local {})",
+               ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
+               p_peer->acceptor ? "acceptor" : "initiator");
 
   bta_av_co_store_peer_codectype(p_peer);
 
@@ -1027,8 +1028,8 @@
     }
     p_sink = p_peer->p_sink;
     if (p_sink == nullptr) {
-      LOG_ERROR("%s: cannot find the selected codec for peer %s", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::error("cannot find the selected codec for peer {}",
+                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       return A2DP_FAIL;
     }
   } else {
@@ -1036,8 +1037,8 @@
       // Apply user preferred codec directly before first codec selected.
       p_sink = FindPeerSink(p_peer, BTAV_A2DP_CODEC_INDEX_SOURCE_SBC);
       if (p_sink != nullptr) {
-        LOG_VERBOSE("%s: mandatory codec preferred for peer %s", __func__,
-                    ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+        log::verbose("mandatory codec preferred for peer {}",
+                     ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
         btav_a2dp_codec_config_t high_priority_mandatory{
             .codec_type = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,
             .codec_priority = BTAV_A2DP_CODEC_PRIORITY_HIGHEST,
@@ -1054,14 +1055,14 @@
             result_codec_config, &restart_input, &restart_output,
             &config_updated);
       } else {
-        LOG_WARN("%s: mandatory codec not found for peer %s", __func__,
-                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+        log::warn("mandatory codec not found for peer {}",
+                  ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       }
     }
     p_sink = SelectSourceCodec(p_peer);
     if (p_sink == nullptr) {
-      LOG_ERROR("%s: cannot set up codec for peer %s", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::error("cannot set up codec for peer {}",
+                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       return A2DP_FAIL;
     }
   }
@@ -1075,14 +1076,14 @@
 
   // If acceptor -> reconfig otherwise reply for configuration
   *p_sep_info_idx = p_sink->sep_info_idx;
-  LOG_VERBOSE("%s: peer %s acceptor:%s reconfig_needed:%s", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
-              (p_peer->acceptor) ? "true" : "false",
-              (p_peer->reconfig_needed) ? "true" : "false");
+  log::verbose("peer {} acceptor:{} reconfig_needed:{}",
+               ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
+               (p_peer->acceptor) ? "true" : "false",
+               (p_peer->reconfig_needed) ? "true" : "false");
   if (p_peer->acceptor) {
     if (p_peer->reconfig_needed) {
-      LOG_VERBOSE("%s: call BTA_AvReconfig(0x%x) for peer %s", __func__,
-                  bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::verbose("call BTA_AvReconfig(0x{:x}) for peer {}", bta_av_handle,
+                   ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       BTA_AvReconfig(bta_av_handle, true, p_sink->sep_info_idx,
                      p_peer->codec_config, *p_num_protect, bta_av_co_cp_scmst);
     }
@@ -1091,8 +1092,8 @@
   }
 
   // report this peer selectable codecs after retrieved all its capabilities.
-  LOG(INFO) << __func__ << ": retrieved " << +p_peer->num_rx_sinks
-            << " capabilities from peer " << p_peer->addr;
+  log::info("retrieved {} capabilities from peer {}", p_peer->num_rx_sinks,
+            ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
   ReportSourceCodecState(p_peer);
 
   return A2DP_SUCCESS;
@@ -1106,26 +1107,24 @@
                                            uint8_t* p_protect_info) {
   std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
-  LOG_VERBOSE("%s: peer %s bta_av_handle:0x%x codec:%s seid:%d", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle,
-              A2DP_CodecName(p_codec_info), seid);
-  LOG_VERBOSE("%s: num_protect:0x%02x protect_info:0x%02x%02x%02x", __func__,
-              *p_num_protect, p_protect_info[0], p_protect_info[1],
-              p_protect_info[2]);
-  LOG_VERBOSE("%s: codec: %s", __func__,
-              A2DP_CodecInfoString(p_codec_info).c_str());
+  log::verbose("peer {} bta_av_handle:0x{:x} codec:{} seid:{}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle,
+               A2DP_CodecName(p_codec_info), seid);
+  log::verbose("num_protect:0x{:02x} protect_info:0x{:02x}{:02x}{:02x}",
+               *p_num_protect, p_protect_info[0], p_protect_info[1],
+               p_protect_info[2]);
+  log::verbose("codec: {}", A2DP_CodecInfoString(p_codec_info));
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    LOG_ERROR("%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-              __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("could not find peer entry for bta_av_handle 0x{:x} peer {}",
+               bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
     return A2DP_FAIL;
   }
-  LOG_VERBOSE(
-      "%s: peer %s found (o=%d, n_sources=%d, n_rx_sources=%d, "
-      "n_sup_sources=%d)",
-      __func__, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr), p_peer->opened,
+  log::verbose(
+      "peer {} found (o={}, n_sources={}, n_rx_sources={}, n_sup_sources={})",
+      ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr), p_peer->opened,
       p_peer->num_sources, p_peer->num_rx_sources, p_peer->num_sup_sources);
 
   p_peer->num_rx_sources++;
@@ -1136,9 +1135,9 @@
     if (p_peer->num_sup_sources < BTA_AV_CO_NUM_ELEMENTS(p_peer->sources)) {
       BtaAvCoSep* p_source = &p_peer->sources[p_peer->num_sup_sources++];
 
-      LOG_VERBOSE("%s: saved caps[%x:%x:%x:%x:%x:%x]", __func__,
-                  p_codec_info[1], p_codec_info[2], p_codec_info[3],
-                  p_codec_info[4], p_codec_info[5], p_codec_info[6]);
+      log::verbose("saved caps[{:x}:{:x}:{:x}:{:x}:{:x}:{:x}]", p_codec_info[1],
+                   p_codec_info[2], p_codec_info[3], p_codec_info[4],
+                   p_codec_info[5], p_codec_info[6]);
 
       memcpy(p_source->codec_caps, p_codec_info, AVDT_CODEC_SIZE);
       p_source->sep_info_idx = *p_sep_info_idx;
@@ -1146,8 +1145,8 @@
       p_source->num_protect = *p_num_protect;
       memcpy(p_source->protect_info, p_protect_info, AVDT_CP_INFO_LEN);
     } else {
-      LOG_ERROR("%s: peer %s : no more room for Source info", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::error("peer {} : no more room for Source info",
+                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
     }
   }
 
@@ -1157,8 +1156,8 @@
       (p_peer->num_sup_sources != BTA_AV_CO_NUM_ELEMENTS(p_peer->sources))) {
     return A2DP_FAIL;
   }
-  LOG_VERBOSE("%s: last Source codec reached for peer %s", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+  log::verbose("last Source codec reached for peer {}",
+               ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
 
   // Select the Sink codec
   const BtaAvCoSep* p_source = nullptr;
@@ -1171,15 +1170,15 @@
     }
     p_source = p_peer->p_source;
     if (p_source == nullptr) {
-      LOG_ERROR("%s: cannot find the selected codec for peer %s", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::error("cannot find the selected codec for peer {}",
+                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       return A2DP_FAIL;
     }
   } else {
     p_source = SelectSinkCodec(p_peer);
     if (p_source == nullptr) {
-      LOG_ERROR("%s: cannot set up codec for the peer %s", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::error("cannot set up codec for the peer {}",
+                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       return A2DP_FAIL;
     }
   }
@@ -1193,14 +1192,14 @@
 
   // If acceptor -> reconfig otherwise reply for configuration
   *p_sep_info_idx = p_source->sep_info_idx;
-  LOG_VERBOSE("%s: peer %s acceptor:%s reconfig_needed:%s", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
-              (p_peer->acceptor) ? "true" : "false",
-              (p_peer->reconfig_needed) ? "true" : "false");
+  log::verbose("peer {} acceptor:{} reconfig_needed:{}",
+               ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
+               (p_peer->acceptor) ? "true" : "false",
+               (p_peer->reconfig_needed) ? "true" : "false");
   if (p_peer->acceptor) {
     if (p_peer->reconfig_needed) {
-      LOG_VERBOSE("%s: call BTA_AvReconfig(0x%x) for peer %s", __func__,
-                  bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::verbose("call BTA_AvReconfig(0x{:x}) for peer {}", bta_av_handle,
+                   ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       BTA_AvReconfig(bta_av_handle, true, p_source->sep_info_idx,
                      p_peer->codec_config, *p_num_protect, bta_av_co_cp_scmst);
     }
@@ -1221,56 +1220,54 @@
   uint8_t category = A2DP_SUCCESS;
   bool reconfig_needed = false;
 
-  LOG_VERBOSE(
-      "%s: bta_av_handle=0x%x peer_address=%s seid=%d "
-      "num_protect=%d t_local_sep=%d avdt_handle=%d",
-      __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address), seid,
-      num_protect, t_local_sep, avdt_handle);
-  LOG_VERBOSE("%s: p_codec_info[%x:%x:%x:%x:%x:%x]", __func__, p_codec_info[1],
-              p_codec_info[2], p_codec_info[3], p_codec_info[4],
-              p_codec_info[5], p_codec_info[6]);
-  LOG_VERBOSE("%s: num_protect:0x%02x protect_info:0x%02x%02x%02x", __func__,
-              num_protect, p_protect_info[0], p_protect_info[1],
-              p_protect_info[2]);
-  LOG_VERBOSE("%s: codec: %s", __func__,
-              A2DP_CodecInfoString(p_codec_info).c_str());
+  log::verbose(
+      "bta_av_handle=0x{:x} peer_address={} seid={} num_protect={} "
+      "t_local_sep={} avdt_handle={}",
+      bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address), seid, num_protect,
+      t_local_sep, avdt_handle);
+  log::verbose("p_codec_info[{:x}:{:x}:{:x}:{:x}:{:x}:{:x}]", p_codec_info[1],
+               p_codec_info[2], p_codec_info[3], p_codec_info[4],
+               p_codec_info[5], p_codec_info[6]);
+  log::verbose("num_protect:0x{:02x} protect_info:0x{:02x}{:02x}{:02x}",
+               num_protect, p_protect_info[0], p_protect_info[1],
+               p_protect_info[2]);
+  log::verbose("codec: {}", A2DP_CodecInfoString(p_codec_info));
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    LOG_ERROR("%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-              __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("could not find peer entry for bta_av_handle 0x{:x} peer {}",
+               bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
     // Call call-in rejecting the configuration
     bta_av_ci_setconfig(bta_av_handle, A2DP_BUSY, AVDT_ASC_CODEC, 0, nullptr,
                         false, avdt_handle);
     return;
   }
 
-  LOG_VERBOSE(
-      "%s: peer %s found (o=%d, n_sinks=%d, n_rx_sinks=%d, "
-      "n_sup_sinks=%d)",
-      __func__, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr), p_peer->opened,
-      p_peer->num_sinks, p_peer->num_rx_sinks, p_peer->num_sup_sinks);
+  log::verbose(
+      "peer {} found (o={}, n_sinks={}, n_rx_sinks={}, n_sup_sinks={})",
+      ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr), p_peer->opened, p_peer->num_sinks,
+      p_peer->num_rx_sinks, p_peer->num_sup_sinks);
 
   // Sanity check: should not be opened at this point
   if (p_peer->opened) {
-    LOG_ERROR("%s: peer %s already in use", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+    log::error("peer {} already in use",
+               ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
   }
 
   if (num_protect != 0) {
     if (ContentProtectEnabled()) {
       if ((num_protect != 1) ||
           !BtaAvCo::ContentProtectIsScmst(p_protect_info)) {
-        LOG_ERROR("%s: wrong CP configuration for peer %s", __func__,
-                  ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+        log::error("wrong CP configuration for peer {}",
+                   ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
         status = A2DP_BAD_CP_TYPE;
         category = AVDT_ASC_PROTECT;
       }
     } else {
       // Do not support content protection for the time being
-      LOG_ERROR("%s: wrong CP configuration for peer %s", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::error("wrong CP configuration for peer {}",
+                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       status = A2DP_BAD_CP_TYPE;
       category = AVDT_ASC_PROTECT;
     }
@@ -1280,8 +1277,8 @@
     bool codec_config_supported = false;
 
     if (t_local_sep == AVDT_TSEP_SNK) {
-      LOG_VERBOSE("%s: peer %s is A2DP Source", __func__,
-                  ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::verbose("peer {} is A2DP Source",
+                   ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       codec_config_supported = A2DP_IsSinkCodecSupported(p_codec_info);
       if (codec_config_supported) {
         // If Peer is Source, and our config subset matches with what is
@@ -1290,17 +1287,17 @@
       }
     }
     if (t_local_sep == AVDT_TSEP_SRC) {
-      LOG_VERBOSE("%s: peer %s is A2DP SINK", __func__,
-                  ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+      log::verbose("peer {} is A2DP SINK",
+                   ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       // Ignore the restart_output flag: accepting the remote device's
       // codec selection should not trigger codec reconfiguration.
       bool dummy_restart_output = false;
       if ((p_peer->GetCodecs() == nullptr) ||
           !SetCodecOtaConfig(p_peer, p_codec_info, num_protect, p_protect_info,
                              &dummy_restart_output)) {
-        LOG_ERROR("%s: cannot set source codec %s for peer %s", __func__,
-                  A2DP_CodecName(p_codec_info),
-                  ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+        log::error("cannot set source codec {} for peer {}",
+                   A2DP_CodecName(p_codec_info),
+                   ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
       } else {
         codec_config_supported = true;
         // Check if reconfiguration is needed
@@ -1318,8 +1315,8 @@
   }
 
   if (status != A2DP_SUCCESS) {
-    LOG_VERBOSE("%s: peer %s reject s=%d c=%d", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr), status, category);
+    log::verbose("peer {} reject s={} c={}",
+                 ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr), status, category);
     // Call call-in rejecting the configuration
     bta_av_ci_setconfig(bta_av_handle, status, category, 0, nullptr, false,
                         avdt_handle);
@@ -1329,8 +1326,8 @@
   // Mark that this is an acceptor peer
   p_peer->acceptor = true;
   p_peer->reconfig_needed = reconfig_needed;
-  LOG_VERBOSE("%s: peer %s accept reconf=%d", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr), reconfig_needed);
+  log::verbose("peer {} accept reconf={}",
+               ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr), reconfig_needed);
   // Call call-in accepting the configuration
   bta_av_ci_setconfig(bta_av_handle, A2DP_SUCCESS, A2DP_SUCCESS, 0, nullptr,
                       reconfig_needed, avdt_handle);
@@ -1338,14 +1335,14 @@
 
 void BtaAvCo::ProcessOpen(tBTA_AV_HNDL bta_av_handle,
                           const RawAddress& peer_address, uint16_t mtu) {
-  LOG_VERBOSE("%s: peer %s bta_av_handle: 0x%x mtu:%d", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle, mtu);
+  log::verbose("peer {} bta_av_handle: 0x{:x} mtu:{}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle, mtu);
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    LOG_ERROR("%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-              __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("could not find peer entry for bta_av_handle 0x{:x} peer {}",
+               bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
     return;
   }
   p_peer->opened = true;
@@ -1359,15 +1356,15 @@
 
 void BtaAvCo::ProcessClose(tBTA_AV_HNDL bta_av_handle,
                            const RawAddress& peer_address) {
-  LOG_VERBOSE("%s: peer %s bta_av_handle: 0x%x", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
+  log::verbose("peer {} bta_av_handle: 0x{:x}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
   btif_av_reset_audio_delay();
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    LOG_ERROR("%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-              __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("could not find peer entry for bta_av_handle 0x{:x} peer {}",
+               bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
     return;
   }
   // Reset the active peer
@@ -1381,29 +1378,29 @@
 void BtaAvCo::ProcessStart(tBTA_AV_HNDL bta_av_handle,
                            const RawAddress& peer_address,
                            const uint8_t* p_codec_info, bool* p_no_rtp_header) {
-  LOG_VERBOSE("%s: peer %s bta_av_handle: 0x%x", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
+  log::verbose("peer {} bta_av_handle: 0x{:x}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    LOG_ERROR("%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-              __func__, bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
+    log::error("could not find peer entry for bta_av_handle 0x{:x} peer {}",
+               bta_av_handle, ADDRESS_TO_LOGGABLE_CSTR(peer_address));
     return;
   }
 
   bool add_rtp_header =
       A2DP_UsesRtpHeader(p_peer->ContentProtectActive(), p_codec_info);
 
-  LOG_VERBOSE("%s: bta_av_handle: 0x%x add_rtp_header: %s", __func__,
-              bta_av_handle, add_rtp_header ? "true" : "false");
+  log::verbose("bta_av_handle: 0x{:x} add_rtp_header: {}", bta_av_handle,
+               add_rtp_header ? "true" : "false");
   *p_no_rtp_header = !add_rtp_header;
 }
 
 void BtaAvCo::ProcessStop(tBTA_AV_HNDL bta_av_handle,
                           const RawAddress& peer_address) {
-  LOG_VERBOSE("%s: peer %s bta_av_handle: 0x%x", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
+  log::verbose("peer {} bta_av_handle: 0x{:x}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
   // Nothing to do
 }
 
@@ -1411,14 +1408,14 @@
                                          uint32_t* p_timestamp) {
   BT_HDR* p_buf;
 
-  LOG_VERBOSE("%s: codec: %s", __func__, A2DP_CodecName(p_codec_info));
+  log::verbose("codec: {}", A2DP_CodecName(p_codec_info));
 
   p_buf = btif_a2dp_source_audio_readbuf();
   if (p_buf == nullptr) return nullptr;
 
   if (p_buf->offset < 4) {
     osi_free(p_buf);
-    LOG_ERROR("No space for timestamp in packet, dropped");
+    log::error("No space for timestamp in packet, dropped");
     return nullptr;
   }
   /*
@@ -1432,8 +1429,7 @@
   if (!A2DP_GetPacketTimestamp(p_codec_info, (const uint8_t*)(p_buf + 1),
                                p_timestamp) ||
       !A2DP_BuildCodecHeader(p_codec_info, p_buf, p_buf->layer_specific)) {
-    LOG_ERROR("%s: unsupported codec type (%d)", __func__,
-              A2DP_GetCodecType(p_codec_info));
+    log::error("unsupported codec type ({})", A2DP_GetCodecType(p_codec_info));
     osi_free(p_buf);
     return nullptr;
   }
@@ -1453,38 +1449,36 @@
 
 void BtaAvCo::DataPacketWasDropped(tBTA_AV_HNDL bta_av_handle,
                                    const RawAddress& peer_address) {
-  LOG_ERROR("%s: peer %s dropped audio packet on handle 0x%x", __func__,
-            ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
+  log::error("peer {} dropped audio packet on handle 0x{:x}",
+             ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle);
 }
 
 void BtaAvCo::ProcessAudioDelay(tBTA_AV_HNDL bta_av_handle,
                                 const RawAddress& peer_address,
                                 uint16_t delay) {
-  LOG_VERBOSE("%s: peer %s bta_av_handle: 0x%x delay:0x%x", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle, delay);
+  log::verbose("peer {} bta_av_handle: 0x{:x} delay:0x{:x}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle, delay);
 
   btif_av_set_audio_delay(peer_address, delay);
 }
 
 void BtaAvCo::UpdateMtu(tBTA_AV_HNDL bta_av_handle,
                         const RawAddress& peer_address, uint16_t mtu) {
-  LOG(INFO) << __func__ << ": peer " << ADDRESS_TO_LOGGABLE_STR(peer_address)
-            << " bta_av_handle: " << loghex(bta_av_handle) << " mtu: " << mtu;
+  log::info("peer {} bta_av_handle: {} mtu: {}",
+            ADDRESS_TO_LOGGABLE_STR(peer_address), loghex(bta_av_handle), mtu);
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    LOG(ERROR) << __func__ << ": could not find peer entry for bta_av_handle "
-               << loghex(bta_av_handle) << " peer "
-               << ADDRESS_TO_LOGGABLE_STR(peer_address);
+    log::error("could not find peer entry for bta_av_handle {} peer {}",
+               loghex(bta_av_handle), ADDRESS_TO_LOGGABLE_STR(peer_address));
     return;
   }
   p_peer->mtu = mtu;
 }
 
 bool BtaAvCo::SetActivePeer(const RawAddress& peer_address) {
-  LOG(INFO) << __func__
-            << ": peer_address=" << ADDRESS_TO_LOGGABLE_STR(peer_address);
+  log::info("peer_address={}", ADDRESS_TO_LOGGABLE_STR(peer_address));
 
   std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
@@ -1503,7 +1497,7 @@
 
   active_peer_ = p_peer;
   memcpy(codec_config_, active_peer_->codec_config, AVDT_CODEC_SIZE);
-  LOG(INFO) << __func__ << ": codec = " << A2DP_CodecInfoString(codec_config_);
+  log::info("codec = {}", A2DP_CodecInfoString(codec_config_));
   // report the selected codec configuration of this new active peer.
   ReportSourceCodecState(active_peer_);
   return true;
@@ -1533,11 +1527,11 @@
   p_peer_params->is_peer_edr = btif_av_is_peer_edr(peer_address);
   p_peer_params->peer_supports_3mbps =
       btif_av_peer_supports_3mbps(peer_address);
-  LOG_VERBOSE(
-      "%s: peer_address=%s peer_mtu=%d is_peer_edr=%s peer_supports_3mbps=%s",
-      __func__, ADDRESS_TO_LOGGABLE_CSTR(peer_address), p_peer_params->peer_mtu,
-      logbool(p_peer_params->is_peer_edr).c_str(),
-      logbool(p_peer_params->peer_supports_3mbps).c_str());
+  log::verbose(
+      "peer_address={} peer_mtu={} is_peer_edr={} peer_supports_3mbps={}",
+      ADDRESS_TO_LOGGABLE_CSTR(peer_address), p_peer_params->peer_mtu,
+      logbool(p_peer_params->is_peer_edr),
+      logbool(p_peer_params->peer_supports_3mbps));
 }
 
 const tA2DP_ENCODER_INTERFACE* BtaAvCo::GetSourceEncoderInterface() {
@@ -1556,17 +1550,16 @@
   bool config_updated = false;
   bool success = true;
 
-  VLOG(1) << __func__ << ": peer_address="
-          << ADDRESS_TO_LOGGABLE_STR(peer_address)
-          << " codec_user_config={" << codec_user_config.ToString() << "}";
+  log::verbose("peer_address={} codec_user_config={{}}",
+               ADDRESS_TO_LOGGABLE_STR(peer_address),
+               codec_user_config.ToString());
 
   *p_restart_output = false;
 
   BtaAvCoPeer* p_peer = FindPeer(peer_address);
   if (p_peer == nullptr) {
-    LOG(ERROR) << __func__ << ": cannot find peer "
-               << ADDRESS_TO_LOGGABLE_STR(peer_address)
-               << " to configure";
+    log::error("cannot find peer {} to configure",
+               ADDRESS_TO_LOGGABLE_STR(peer_address));
     success = false;
     goto done;
   }
@@ -1574,9 +1567,8 @@
   // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
   if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
       (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
-    LOG(WARNING) << __func__ << ": peer "
-                 << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-                 << " : not all peer's capabilities have been retrieved";
+    log::warn("peer {} : not all peer's capabilities have been retrieved",
+              ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
     success = false;
     goto done;
   }
@@ -1589,9 +1581,9 @@
     p_sink = p_peer->p_sink;
   }
   if (p_sink == nullptr) {
-    LOG(ERROR) << __func__ << ": peer " << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-               << " : cannot find peer SEP to configure for codec type "
-               << codec_user_config.codec_type;
+    log::error("peer {} : cannot find peer SEP to configure for codec type {}",
+               ADDRESS_TO_LOGGABLE_STR(p_peer->addr),
+               codec_user_config.codec_type);
     success = false;
     goto done;
   }
@@ -1614,16 +1606,14 @@
 
     p_sink = SelectSourceCodec(p_peer);
     if (p_sink == nullptr) {
-      LOG(ERROR) << __func__ << ": peer "
-                 << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-                 << " : cannot set up codec for the peer SINK";
+      log::error("peer {} : cannot set up codec for the peer SINK",
+                 ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
       success = false;
       goto done;
     }
 
     p_peer->acceptor = false;
-    VLOG(1) << __func__ << ": call BTA_AvReconfig("
-            << loghex(p_peer->BtaAvHandle()) << ")";
+    log::verbose("call BTA_AvReconfig({})", loghex(p_peer->BtaAvHandle()));
     BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx,
                    p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
     *p_restart_output = true;
@@ -1652,31 +1642,28 @@
   bool restart_output = false;
   bool config_updated = false;
 
-  VLOG(1) << __func__
-          << ": codec_audio_config: " << codec_audio_config.ToString();
+  log::verbose("codec_audio_config: {}", codec_audio_config.ToString());
 
   // Find the peer that is currently open
   BtaAvCoPeer* p_peer = active_peer_;
   if (p_peer == nullptr) {
-    LOG(ERROR) << __func__ << ": no active peer to configure";
+    log::error("no active peer to configure");
     return false;
   }
 
   // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
   if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
       (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
-    LOG(WARNING) << __func__ << ": peer "
-                 << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-                 << " : not all peer's capabilities have been retrieved";
+    log::warn("peer {} : not all peer's capabilities have been retrieved",
+              ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
     return false;
   }
 
   // Use the current sink codec
   const BtaAvCoSep* p_sink = p_peer->p_sink;
   if (p_sink == nullptr) {
-    LOG(ERROR) << __func__ << ": peer "
-               << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-               << " : cannot find peer SEP to configure";
+    log::error("peer {} : cannot find peer SEP to configure",
+               ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
     return false;
   }
 
@@ -1698,8 +1685,7 @@
                        p_sink->protect_info);
 
     p_peer->acceptor = false;
-    VLOG(1) << __func__ << ": call BTA_AvReconfig("
-            << loghex(p_peer->BtaAvHandle()) << ")";
+    log::verbose("call BTA_AvReconfig({})", loghex(p_peer->BtaAvHandle()));
     BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx,
                    p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
   }
@@ -1734,25 +1720,23 @@
   std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities;
   std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities;
 
-  VLOG(1) << __func__ << ": peer_address="
-          << ADDRESS_TO_LOGGABLE_STR(p_peer->addr);
+  log::verbose("peer_address={}", ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
   A2dpCodecs* codecs = p_peer->GetCodecs();
   if (codecs == nullptr) {
-    LOG_ERROR("Peer codecs is set to null");
+    log::error("Peer codecs is set to null");
     return false;
   }
   if (!codecs->getCodecConfigAndCapabilities(&codec_config,
                                              &codecs_local_capabilities,
                                              &codecs_selectable_capabilities)) {
-    LOG(WARNING) << __func__ << ": Peer "
-                 << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-                 << " : error reporting audio source codec state: cannot get "
-                    "codec config and capabilities";
+    log::warn(
+        "Peer {} : error reporting audio source codec state: cannot get codec "
+        "config and capabilities",
+        ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
     return false;
   }
-  LOG(INFO) << __func__ << ": peer "
-            << ADDRESS_TO_LOGGABLE_STR(p_peer->addr) << " codec_config={"
-            << codec_config.ToString() << "}";
+  log::info("peer {} codec_config={{}}", ADDRESS_TO_LOGGABLE_STR(p_peer->addr),
+            codec_config.ToString());
   btif_av_report_source_codec_state(p_peer->addr, codec_config,
                                     codecs_local_capabilities,
                                     codecs_selectable_capabilities);
@@ -1760,8 +1744,7 @@
 }
 
 bool BtaAvCo::ReportSinkCodecState(BtaAvCoPeer* p_peer) {
-  LOG_VERBOSE("%s: peer_address=%s", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+  log::verbose("peer_address={}", ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
   // Nothing to do (for now)
   return true;
 }
@@ -1809,14 +1792,14 @@
 }
 
 bool BtaAvCo::ContentProtectIsScmst(const uint8_t* p_protect_info) {
-  LOG_VERBOSE("%s", __func__);
+  log::verbose("");
 
   if (*p_protect_info >= AVDT_CP_LOSC) {
     uint16_t cp_id;
     p_protect_info++;
     STREAM_TO_UINT16(cp_id, p_protect_info);
     if (cp_id == AVDT_CP_SCMS_T_ID) {
-      LOG_VERBOSE("%s: SCMS-T found", __func__);
+      log::verbose("SCMS-T found");
       return true;
     }
   }
@@ -1825,18 +1808,18 @@
 
 bool BtaAvCo::AudioProtectHasScmst(uint8_t num_protect,
                                    const uint8_t* p_protect_info) {
-  LOG_VERBOSE("%s", __func__);
+  log::verbose("");
   while (num_protect--) {
     if (BtaAvCo::ContentProtectIsScmst(p_protect_info)) return true;
     // Move to the next Content Protect schema
     p_protect_info += *p_protect_info + 1;
   }
-  LOG_VERBOSE("%s: SCMS-T not found", __func__);
+  log::verbose("SCMS-T not found");
   return false;
 }
 
 bool BtaAvCo::AudioSepHasContentProtection(const BtaAvCoSep* p_sep) {
-  LOG_VERBOSE("%s", __func__);
+  log::verbose("");
 
   // Check if content protection is enabled for this stream
   if (ContentProtectFlag() != AVDT_CP_SCMS_COPY_FREE) {
@@ -1844,7 +1827,7 @@
                                          p_sep->protect_info);
   }
 
-  LOG_VERBOSE("%s: not required", __func__);
+  log::verbose("not required");
   return true;
 }
 
@@ -1878,7 +1861,7 @@
   // AttemptSourceCodecSelection, except the configuration
   // is provided by the HAL rather than derived locally.
 
-  LOG_INFO("Configuration=%s", provider_codec_config.toString().c_str());
+  log::info("Configuration={}", provider_codec_config.toString());
 
   // Identify the selected sink.
   auto* p_sink =
@@ -1929,16 +1912,16 @@
     const BtaAvCoSep* p_sink = FindPeerSink(p_peer, iter->codecIndex());
 
     if (p_sink == nullptr) {
-      LOG_VERBOSE("peer Sink for codec %s not found", iter->name().c_str());
+      log::verbose("peer Sink for codec {} not found", iter->name());
       continue;
     }
 
     if (!p_peer->GetCodecs()->setCodecConfig(
             p_sink->codec_caps, true /* is_capability */, new_codec_config,
             false /* select_current_codec */)) {
-      LOG_VERBOSE("cannot set source codec %s", iter->name().c_str());
+      log::verbose("cannot set source codec {}", iter->name());
     } else {
-      LOG_VERBOSE("feasible to set source codec %s", iter->name().c_str());
+      log::verbose("feasible to set source codec {}", iter->name());
       software_codec_config = iter;
       break;
     }
@@ -1971,13 +1954,13 @@
 
   // Select the codec
   for (const auto& iter : p_peer->GetCodecs()->orderedSinkCodecs()) {
-    LOG_VERBOSE("%s: trying codec %s", __func__, iter->name().c_str());
+    log::verbose("trying codec {}", iter->name());
     p_source = AttemptSinkCodecSelection(*iter, p_peer);
     if (p_source != nullptr) {
-      LOG_VERBOSE("%s: selected codec %s", __func__, iter->name().c_str());
+      log::verbose("selected codec {}", iter->name());
       break;
     }
-    LOG_VERBOSE("%s: cannot use codec %s", __func__, iter->name().c_str());
+    log::verbose("cannot use codec {}", iter->name());
   }
 
   // NOTE: Unconditionally dispatch the event to make sure a callback with
@@ -1990,8 +1973,8 @@
 BtaAvCoSep* BtaAvCo::FindPeerSink(BtaAvCoPeer* p_peer,
                                   btav_a2dp_codec_index_t codec_index) {
   if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) {
-    LOG_WARN("%s: invalid codec index for peer %s", __func__,
-             ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+    log::warn("invalid codec index for peer {}",
+              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
     return nullptr;
   }
 
@@ -2004,10 +1987,8 @@
       continue;
     }
     if (!AudioSepHasContentProtection(p_sink)) {
-      LOG_VERBOSE(
-          "%s: peer Sink for codec %s does not support "
-          "Content Protection",
-          __func__, A2DP_CodecIndexStr(codec_index));
+      log::verbose("peer Sink for codec {} does not support Content Protection",
+                   A2DP_CodecIndexStr(codec_index));
       continue;
     }
     return p_sink;
@@ -2018,8 +1999,8 @@
 BtaAvCoSep* BtaAvCo::FindPeerSource(BtaAvCoPeer* p_peer,
                                     btav_a2dp_codec_index_t codec_index) {
   if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) {
-    LOG_WARN("%s: invalid codec index for peer %s", __func__,
-             ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+    log::warn("invalid codec index for peer {}",
+              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
     return nullptr;
   }
 
@@ -2032,10 +2013,9 @@
       continue;
     }
     if (!AudioSepHasContentProtection(p_source)) {
-      LOG_VERBOSE(
-          "%s: peer Source for codec %s does not support "
-          "Content Protection",
-          __func__, A2DP_CodecIndexStr(codec_index));
+      log::verbose(
+          "peer Source for codec {} does not support Content Protection",
+          A2DP_CodecIndexStr(codec_index));
       continue;
     }
     return p_source;
@@ -2047,20 +2027,18 @@
     const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer) {
   uint8_t new_codec_config[AVDT_CODEC_SIZE];
 
-  LOG_VERBOSE("%s", __func__);
+  log::verbose("");
 
   // Find the peer Sink for the codec
   BtaAvCoSep* p_sink = FindPeerSink(p_peer, codec_config.codecIndex());
   if (p_sink == nullptr) {
-    LOG_VERBOSE("%s: peer Sink for codec %s not found", __func__,
-                codec_config.name().c_str());
+    log::verbose("peer Sink for codec {} not found", codec_config.name());
     return nullptr;
   }
   if (!p_peer->GetCodecs()->setCodecConfig(
           p_sink->codec_caps, true /* is_capability */, new_codec_config,
           true /* select_current_codec */)) {
-    LOG_VERBOSE("%s: cannot set source codec %s", __func__,
-                codec_config.name().c_str());
+    log::verbose("cannot set source codec {}", codec_config.name());
     return nullptr;
   }
   p_peer->p_sink = p_sink;
@@ -2075,20 +2053,18 @@
     const A2dpCodecConfig& codec_config, BtaAvCoPeer* p_peer) {
   uint8_t new_codec_config[AVDT_CODEC_SIZE];
 
-  LOG_VERBOSE("%s", __func__);
+  log::verbose("");
 
   // Find the peer Source for the codec
   BtaAvCoSep* p_source = FindPeerSource(p_peer, codec_config.codecIndex());
   if (p_source == nullptr) {
-    LOG_VERBOSE("%s: peer Source for codec %s not found", __func__,
-                codec_config.name().c_str());
+    log::verbose("peer Source for codec {} not found", codec_config.name());
     return nullptr;
   }
   if (!p_peer->GetCodecs()->setSinkCodecConfig(
           p_source->codec_caps, true /* is_capability */, new_codec_config,
           true /* select_current_codec */)) {
-    LOG_VERBOSE("%s: cannot set sink codec %s", __func__,
-                codec_config.name().c_str());
+    log::verbose("cannot set sink codec {}", codec_config.name());
     return nullptr;
   }
   p_peer->p_source = p_source;
@@ -2100,12 +2076,11 @@
 }
 
 size_t BtaAvCo::UpdateAllSelectableSourceCodecs(BtaAvCoPeer* p_peer) {
-  LOG_VERBOSE("%s: peer %s", __func__, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+  log::verbose("peer {}", ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
 
   size_t updated_codecs = 0;
   for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) {
-    LOG_VERBOSE("%s: updating selectable codec %s", __func__,
-                iter->name().c_str());
+    log::verbose("updating selectable codec {}", iter->name());
     if (UpdateSelectableSourceCodec(*iter, p_peer)) {
       updated_codecs++;
     }
@@ -2115,7 +2090,7 @@
 
 bool BtaAvCo::UpdateSelectableSourceCodec(const A2dpCodecConfig& codec_config,
                                           BtaAvCoPeer* p_peer) {
-  LOG_VERBOSE("%s: peer %s", __func__, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+  log::verbose("peer {}", ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
 
   // Find the peer Sink for the codec
   const BtaAvCoSep* p_sink = FindPeerSink(p_peer, codec_config.codecIndex());
@@ -2124,21 +2099,20 @@
     return false;
   }
   if (!p_peer->GetCodecs()->setPeerSinkCodecCapabilities(p_sink->codec_caps)) {
-    LOG_WARN("%s: cannot update peer %s codec capabilities for %s", __func__,
-             ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
-             A2DP_CodecName(p_sink->codec_caps));
+    log::warn("cannot update peer {} codec capabilities for {}",
+              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
+              A2DP_CodecName(p_sink->codec_caps));
     return false;
   }
   return true;
 }
 
 size_t BtaAvCo::UpdateAllSelectableSinkCodecs(BtaAvCoPeer* p_peer) {
-  LOG_VERBOSE("%s: peer %s", __func__, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+  log::verbose("peer {}", ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
 
   size_t updated_codecs = 0;
   for (const auto& iter : p_peer->GetCodecs()->orderedSinkCodecs()) {
-    LOG_VERBOSE("%s: updating selectable codec %s", __func__,
-                iter->name().c_str());
+    log::verbose("updating selectable codec {}", iter->name());
     if (UpdateSelectableSinkCodec(*iter, p_peer)) {
       updated_codecs++;
     }
@@ -2148,7 +2122,7 @@
 
 bool BtaAvCo::UpdateSelectableSinkCodec(const A2dpCodecConfig& codec_config,
                                         BtaAvCoPeer* p_peer) {
-  LOG_VERBOSE("%s: peer %s", __func__, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+  log::verbose("peer {}", ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
 
   // Find the peer Source for the codec
   const BtaAvCoSep* p_source =
@@ -2159,9 +2133,9 @@
   }
   if (!p_peer->GetCodecs()->setPeerSourceCodecCapabilities(
           p_source->codec_caps)) {
-    LOG_WARN("%s: cannot update peer %s codec capabilities for %s", __func__,
-             ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
-             A2DP_CodecName(p_source->codec_caps));
+    log::warn("cannot update peer {} codec capabilities for {}",
+              ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
+              A2DP_CodecName(p_source->codec_caps));
     return false;
   }
   return true;
@@ -2171,9 +2145,8 @@
                                  const uint8_t* new_codec_config,
                                  uint8_t num_protect,
                                  const uint8_t* p_protect_info) {
-  LOG_VERBOSE("%s: peer %s", __func__, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
-  LOG_VERBOSE("%s: codec: %s", __func__,
-              A2DP_CodecInfoString(new_codec_config).c_str());
+  log::verbose("peer {}", ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr));
+  log::verbose("codec: {}", A2DP_CodecInfoString(new_codec_config));
 
   std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
@@ -2197,9 +2170,8 @@
   bool restart_output = false;
   bool config_updated = false;
 
-  LOG(INFO) << __func__ << ": peer_address="
-            << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-            << ", codec: " << A2DP_CodecInfoString(p_ota_codec_config);
+  log::info("peer_address={}, codec: {}", ADDRESS_TO_LOGGABLE_STR(p_peer->addr),
+            A2DP_CodecInfoString(p_ota_codec_config));
 
   *p_restart_output = false;
 
@@ -2210,9 +2182,8 @@
     // There are no peer SEPs if we didn't do the discovery procedure yet.
     // We have all the information we need from the peer, so we can
     // proceed with the OTA codec configuration.
-    LOG(ERROR) << __func__ << ": peer "
-               << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-               << " : cannot find peer SEP to configure";
+    log::error("peer {} : cannot find peer SEP to configure",
+               ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
     return false;
   }
 
@@ -2221,15 +2192,14 @@
   if (!p_peer->GetCodecs()->setCodecOtaConfig(
           p_ota_codec_config, &peer_params, result_codec_config, &restart_input,
           &restart_output, &config_updated)) {
-    LOG(ERROR) << __func__ << ": peer "
-               << ADDRESS_TO_LOGGABLE_STR(p_peer->addr)
-               << " : cannot set OTA config";
+    log::error("peer {} : cannot set OTA config",
+               ADDRESS_TO_LOGGABLE_STR(p_peer->addr));
     return false;
   }
 
   if (restart_output) {
-    VLOG(1) << __func__ << ": restart output for codec: "
-            << A2DP_CodecInfoString(result_codec_config);
+    log::verbose("restart output for codec: {}",
+                 A2DP_CodecInfoString(result_codec_config));
 
     *p_restart_output = true;
     p_peer->p_sink = p_sink;
@@ -2281,7 +2251,7 @@
 static void bta_av_co_store_peer_codectype(const BtaAvCoPeer* p_peer) {
   int index, peer_codec_type = 0;
   const BtaAvCoSep* p_sink;
-  LOG_VERBOSE("%s", __func__);
+  log::verbose("");
   for (index = 0; index < p_peer->num_sup_sinks; index++) {
     p_sink = &p_peer->sinks[index];
     peer_codec_type |= A2DP_IotGetPeerSinkCodecType(p_sink->codec_caps);
@@ -2302,44 +2272,44 @@
   // Prioritize any offload codec except SBC and AAC
   if (A2DP_GetCodecType(hardware_config.codec_config) ==
       A2DP_MEDIA_CT_NON_A2DP) {
-    LOG_VERBOSE("select hardware codec: %s",
-                A2DP_CodecIndexStr(hardware_offload_index));
+    log::verbose("select hardware codec: {}",
+                 A2DP_CodecIndexStr(hardware_offload_index));
     return true;
   }
   // Prioritize LDAC, AptX HD and AptX over AAC and SBC offload codecs
   if (software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC ||
       software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD ||
       software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX) {
-    LOG_VERBOSE("select software codec: %s",
-                A2DP_CodecIndexStr(software_codec_index));
+    log::verbose("select software codec: {}",
+                 A2DP_CodecIndexStr(software_codec_index));
     return false;
   }
   // Prioritize AAC offload
   if (hardware_offload_index == BTAV_A2DP_CODEC_INDEX_SOURCE_AAC) {
-    LOG_VERBOSE("select hardware codec: %s",
-                A2DP_CodecIndexStr(hardware_offload_index));
+    log::verbose("select hardware codec: {}",
+                 A2DP_CodecIndexStr(hardware_offload_index));
     return true;
   }
   // Prioritize AAC software
   if (software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_AAC) {
-    LOG_VERBOSE("select software codec: %s",
-                A2DP_CodecIndexStr(software_codec_index));
+    log::verbose("select software codec: {}",
+                 A2DP_CodecIndexStr(software_codec_index));
     return false;
   }
   // Prioritize SBC offload
   if (hardware_offload_index == BTAV_A2DP_CODEC_INDEX_SOURCE_SBC) {
-    LOG_VERBOSE("select hardware codec: %s",
-                A2DP_CodecIndexStr(hardware_offload_index));
+    log::verbose("select hardware codec: {}",
+                 A2DP_CodecIndexStr(hardware_offload_index));
     return true;
   }
   // Prioritize SBC software
   if (software_codec_index == BTAV_A2DP_CODEC_INDEX_SOURCE_SBC) {
-    LOG_VERBOSE("select software codec: %s",
-                A2DP_CodecIndexStr(software_codec_index));
+    log::verbose("select software codec: {}",
+                 A2DP_CodecIndexStr(software_codec_index));
     return false;
   }
-  LOG_ERROR("select unknown software codec: %s",
-            A2DP_CodecIndexStr(software_codec_index));
+  log::error("select unknown software codec: {}",
+             A2DP_CodecIndexStr(software_codec_index));
   return false;
 }
 
@@ -2351,8 +2321,9 @@
                                        uint8_t* p_protect_info) {
   uint16_t peer_uuid = bta_av_co_cb.FindPeerUuid(bta_av_handle);
 
-  LOG_VERBOSE("%s: peer %s bta_av_handle=0x%x peer_uuid=0x%x", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle, peer_uuid);
+  log::verbose("peer {} bta_av_handle=0x{:x} peer_uuid=0x{:x}",
+               ADDRESS_TO_LOGGABLE_CSTR(peer_address), bta_av_handle,
+               peer_uuid);
 
   switch (peer_uuid) {
     case UUID_SERVCLASS_AUDIO_SOURCE:
@@ -2366,9 +2337,8 @@
     default:
       break;
   }
-  LOG_ERROR("%s: peer %s : Invalid peer UUID: 0x%x for bta_av_handle 0x%x",
-            __func__, ADDRESS_TO_LOGGABLE_CSTR(peer_address), peer_uuid,
-            bta_av_handle);
+  log::error("peer {} : Invalid peer UUID: 0x{:x} for bta_av_handle 0x{:x}",
+             ADDRESS_TO_LOGGABLE_CSTR(peer_address), peer_uuid, bta_av_handle);
   return A2DP_FAIL;
 }
 
diff --git a/system/btif/co/bta_hh_co.cc b/system/btif/co/bta_hh_co.cc
index 8884857f..7b6a6e1 100644
--- a/system/btif/co/bta_hh_co.cc
+++ b/system/btif/co/bta_hh_co.cc
@@ -53,6 +53,8 @@
 /* Max number of polling interrupt allowed */
 #define BTA_HH_UHID_INTERRUPT_COUNT_MAX 100
 
+using namespace bluetooth;
+
 static const bthh_report_type_t map_rtype_uhid_hh[] = {
     BTHH_FEATURE_REPORT, BTHH_OUTPUT_REPORT, BTHH_INPUT_REPORT};
 
@@ -60,27 +62,25 @@
 
 void uhid_set_non_blocking(int fd) {
   int opts = fcntl(fd, F_GETFL);
-  if (opts < 0)
-    LOG_ERROR("%s() Getting flags failed (%s)", __func__, strerror(errno));
+  if (opts < 0) log::error("Getting flags failed ({})", strerror(errno));
 
   opts |= O_NONBLOCK;
 
   if (fcntl(fd, F_SETFL, opts) < 0)
-    LOG_VERBOSE("%s() Setting non-blocking flag failed (%s)", __func__,
-                strerror(errno));
+    log::verbose("Setting non-blocking flag failed ({})", strerror(errno));
 }
 
 static bool uhid_feature_req_handler(btif_hh_device_t* p_dev,
                                      struct uhid_feature_req& req) {
-  LOG_DEBUG("Report type = %d, id = %d", req.rtype, req.rnum);
+  log::debug("Report type = {}, id = {}", req.rtype, req.rnum);
 
   if (req.rtype > UHID_INPUT_REPORT) {
-    LOG_ERROR("Invalid report type %d", req.rtype);
+    log::error("Invalid report type {}", req.rtype);
     return false;
   }
 
   if (p_dev->get_rpt_id_queue == nullptr) {
-    LOG_ERROR("Queue is not initialized");
+    log::error("Queue is not initialized");
     return false;
   }
 
@@ -89,7 +89,7 @@
 
   if (!fixed_queue_try_enqueue(p_dev->get_rpt_id_queue, (void*)context)) {
     osi_free(context);
-    LOG_ERROR("Queue is full, dropping event %d", req.id);
+    log::error("Queue is full, dropping event {}", req.id);
     return false;
   }
 
@@ -100,15 +100,15 @@
 #if ENABLE_UHID_SET_REPORT
 static bool uhid_set_report_req_handler(btif_hh_device_t* p_dev,
                                         struct uhid_set_report_req& req) {
-  LOG_DEBUG("Report type = %d, id = %d", req.rtype, req.rnum);
+  log::debug("Report type = {}, id = {}", req.rtype, req.rnum);
 
   if (req.rtype > UHID_INPUT_REPORT) {
-    LOG_ERROR("Invalid report type %d", req.rtype);
+    log::error("Invalid report type {}", req.rtype);
     return false;
   }
 
   if (p_dev->set_rpt_id_queue == nullptr) {
-    LOG_ERROR("Queue is not initialized");
+    log::error("Queue is not initialized");
     return false;
   }
 
@@ -117,7 +117,7 @@
 
   if (!fixed_queue_try_enqueue(p_dev->set_rpt_id_queue, (void*)context)) {
     osi_free(context);
-    LOG_ERROR("Queue is full, dropping event %d", req.id);
+    log::error("Queue is full, dropping event {}", req.id);
     return false;
   }
 
@@ -133,11 +133,10 @@
 
   if (ret < 0) {
     int rtn = -errno;
-    LOG_ERROR("%s: Cannot write to uhid:%s", __func__, strerror(errno));
+    log::error("Cannot write to uhid:{}", strerror(errno));
     return rtn;
   } else if (ret != (ssize_t)sizeof(*ev)) {
-    LOG_ERROR("%s: Wrong size written to uhid: %zd != %zu", __func__, ret,
-              sizeof(*ev));
+    log::error("Wrong size written to uhid: {} != {}", ret, sizeof(*ev));
     return -EFAULT;
   }
 
@@ -155,39 +154,39 @@
   OSI_NO_INTR(ret = read(p_dev->fd, &ev, sizeof(ev)));
 
   if (ret == 0) {
-    LOG_ERROR("%s: Read HUP on uhid-cdev %s", __func__, strerror(errno));
+    log::error("Read HUP on uhid-cdev {}", strerror(errno));
     return -EFAULT;
   } else if (ret < 0) {
-    LOG_ERROR("%s: Cannot read uhid-cdev: %s", __func__, strerror(errno));
+    log::error("Cannot read uhid-cdev: {}", strerror(errno));
     return -errno;
   }
 
   switch (ev.type) {
     case UHID_START:
-      LOG_VERBOSE("UHID_START from uhid-dev\n");
+      log::verbose("UHID_START from uhid-dev\n");
       p_dev->ready_for_data = true;
       break;
     case UHID_STOP:
-      LOG_VERBOSE("UHID_STOP from uhid-dev\n");
+      log::verbose("UHID_STOP from uhid-dev\n");
       p_dev->ready_for_data = false;
       break;
     case UHID_OPEN:
-      LOG_VERBOSE("UHID_OPEN from uhid-dev\n");
+      log::verbose("UHID_OPEN from uhid-dev\n");
       p_dev->ready_for_data = true;
       break;
     case UHID_CLOSE:
-      LOG_VERBOSE("UHID_CLOSE from uhid-dev\n");
+      log::verbose("UHID_CLOSE from uhid-dev\n");
       p_dev->ready_for_data = false;
       break;
     case UHID_OUTPUT:
       if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.output))) {
-        LOG_ERROR("%s: Invalid size read from uhid-dev: %zd < %zu", __func__,
-                  ret, sizeof(ev.type) + sizeof(ev.u.output));
+        log::error("Invalid size read from uhid-dev: {} < {}", ret,
+                   sizeof(ev.type) + sizeof(ev.u.output));
         return -EFAULT;
       }
 
-      LOG_VERBOSE("UHID_OUTPUT: Report type = %d, report_size = %d",
-                  ev.u.output.rtype, ev.u.output.size);
+      log::verbose("UHID_OUTPUT: Report type = {}, report_size = {}",
+                   ev.u.output.rtype, ev.u.output.size);
       // Send SET_REPORT with feature report if the report type in output event
       // is FEATURE
       if (ev.u.output.rtype == UHID_FEATURE_REPORT)
@@ -196,22 +195,21 @@
       else if (ev.u.output.rtype == UHID_OUTPUT_REPORT)
         btif_hh_senddata(p_dev, ev.u.output.size, ev.u.output.data);
       else
-        LOG_ERROR("%s: UHID_OUTPUT: Invalid report type = %d", __func__,
-                  ev.u.output.rtype);
+        log::error("UHID_OUTPUT: Invalid report type = {}", ev.u.output.rtype);
       break;
     case UHID_OUTPUT_EV:
       if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.output_ev))) {
-        LOG_ERROR("%s: Invalid size read from uhid-dev: %zd < %zu", __func__,
-                  ret, sizeof(ev.type) + sizeof(ev.u.output_ev));
+        log::error("Invalid size read from uhid-dev: {} < {}", ret,
+                   sizeof(ev.type) + sizeof(ev.u.output_ev));
         return -EFAULT;
       }
-      LOG_VERBOSE("UHID_OUTPUT_EV from uhid-dev\n");
+      log::verbose("UHID_OUTPUT_EV from uhid-dev\n");
       break;
 
     case UHID_FEATURE:  // UHID_GET_REPORT
       if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.feature))) {
-        LOG_ERROR("UHID_GET_REPORT: Invalid size read from uhid-dev: %zd < %zu",
-                  ret, sizeof(ev.type) + sizeof(ev.u.feature));
+        log::error("UHID_GET_REPORT: Invalid size read from uhid-dev: {} < {}",
+                   ret, sizeof(ev.type) + sizeof(ev.u.feature));
         return -EFAULT;
       }
 
@@ -224,8 +222,8 @@
 #if ENABLE_UHID_SET_REPORT
     case UHID_SET_REPORT: {
       if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.set_report))) {
-        LOG_ERROR("UHID_SET_REPORT: Invalid size read from uhid-dev: %zd < %zu",
-                  ret, sizeof(ev.type) + sizeof(ev.u.set_report));
+        log::error("UHID_SET_REPORT: Invalid size read from uhid-dev: {} < {}",
+                   ret, sizeof(ev.type) + sizeof(ev.u.set_report));
         return -EFAULT;
       }
 
@@ -237,7 +235,7 @@
 #endif  // ENABLE_UHID_SET_REPORT
 
     default:
-      LOG_ERROR("Invalid event from uhid-dev: %u\n", ev.type);
+      log::error("Invalid event from uhid-dev: {}\n", ev.type);
   }
 
   return 0;
@@ -254,17 +252,17 @@
  ******************************************************************************/
 static inline pthread_t create_thread(void* (*start_routine)(void*),
                                       void* arg) {
-  LOG_VERBOSE("create_thread: entered");
+  log::verbose("create_thread: entered");
   pthread_attr_t thread_attr;
 
   pthread_attr_init(&thread_attr);
   pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
   pthread_t thread_id = -1;
   if (pthread_create(&thread_id, &thread_attr, start_routine, arg) != 0) {
-    LOG_ERROR("pthread_create : %s", strerror(errno));
+    log::error("pthread_create : {}", strerror(errno));
     return -1;
   }
-  LOG_VERBOSE("create_thread: thread created successfully");
+  log::verbose("create_thread: thread created successfully");
   return thread_id;
 }
 
@@ -274,8 +272,8 @@
     struct uhid_event ev = {};
     ev.type = UHID_DESTROY;
     uhid_write(p_dev->fd, &ev);
-    LOG_DEBUG("Closing fd=%d, addr:%s", p_dev->fd,
-              ADDRESS_TO_LOGGABLE_CSTR(p_dev->link_spec));
+    log::debug("Closing fd={}, addr:{}", p_dev->fd,
+               ADDRESS_TO_LOGGABLE_CSTR(p_dev->link_spec));
     close(p_dev->fd);
     p_dev->fd = -1;
   }
@@ -286,7 +284,7 @@
   if (p_dev->fd < 0) {
     p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC);
     if (p_dev->fd < 0) {
-      LOG_ERROR("Failed to open uhid, err:%s", strerror(errno));
+      log::error("Failed to open uhid, err:{}", strerror(errno));
       return false;
     }
   }
@@ -317,7 +315,7 @@
   struct sched_param sched_params;
   sched_params.sched_priority = THREAD_NORMAL_PRIORITY;
   if (sched_setscheduler(pid, SCHED_OTHER, &sched_params)) {
-    LOG_ERROR("Failed to set thread priority to normal: %s", strerror(errno));
+    log::error("Failed to set thread priority to normal: {}", strerror(errno));
     p_dev->hh_poll_thread_id = -1;
     p_dev->hh_keep_polling = 0;
     uhid_fd_close(p_dev);
@@ -325,8 +323,8 @@
   }
 
   pthread_setname_np(pthread_self(), BT_HH_THREAD);
-  LOG_DEBUG("Host hid polling thread created name:%s pid:%d fd:%d",
-            BT_HH_THREAD, pid, p_dev->fd);
+  log::debug("Host hid polling thread created name:{} pid:{} fd:{}",
+             BT_HH_THREAD, pid, p_dev->fd);
 
   pfds[0].fd = p_dev->fd;
   pfds[0].events = POLLIN;
@@ -340,29 +338,29 @@
 
     do {
       if (counter++ > BTA_HH_UHID_INTERRUPT_COUNT_MAX) {
-        LOG_ERROR("Polling interrupted");
+        log::error("Polling interrupted");
         break;
       }
       ret = poll(pfds, 1, BTA_HH_UHID_POLL_PERIOD_MS);
     } while (ret == -1 && errno == EINTR);
 
     if (ret < 0) {
-      LOG_ERROR("Cannot poll for fds: %s\n", strerror(errno));
+      log::error("Cannot poll for fds: {}\n", strerror(errno));
       break;
     }
     if (pfds[0].revents & POLLIN) {
-      LOG_VERBOSE("%s: POLLIN", __func__);
+      log::verbose("POLLIN");
       ret = uhid_read_event(p_dev);
       if (ret != 0) {
-        LOG_ERROR("Unhandled UHID event");
+        log::error("Unhandled UHID event");
         break;
       }
     }
   }
 
   /* Todo: Disconnect if loop exited due to a failure */
-  LOG_INFO("Polling thread stopped for device %s",
-           ADDRESS_TO_LOGGABLE_CSTR(p_dev->link_spec));
+  log::info("Polling thread stopped for device {}",
+            ADDRESS_TO_LOGGABLE_CSTR(p_dev->link_spec));
   p_dev->hh_poll_thread_id = -1;
   p_dev->hh_keep_polling = 0;
   uhid_fd_close(p_dev);
@@ -370,14 +368,14 @@
 }
 
 int bta_hh_co_write(int fd, uint8_t* rpt, uint16_t len) {
-  LOG_VERBOSE("%s: UHID write %d", __func__, len);
+  log::verbose("UHID write {}", len);
 
   struct uhid_event ev;
   memset(&ev, 0, sizeof(ev));
   ev.type = UHID_INPUT;
   ev.u.input.size = len;
   if (len > sizeof(ev.u.input.data)) {
-    LOG_WARN("%s: Report size greater than allowed size", __func__);
+    log::warn("Report size greater than allowed size");
     return -1;
   }
   memcpy(ev.u.input.data, rpt, len);
@@ -400,7 +398,7 @@
   btif_hh_device_t* p_dev = NULL;
 
   if (dev_handle == BTA_HH_INVALID_HANDLE) {
-    LOG_WARN("dev_handle (%d) is invalid", dev_handle);
+    log::warn("dev_handle ({}) is invalid", dev_handle);
     return false;
   }
 
@@ -409,9 +407,9 @@
     if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN &&
         p_dev->dev_handle == dev_handle) {
       // We found a device with the same handle. Must be a device reconnected.
-      LOG_INFO(
-          "Found an existing device with the same handle dev_status=%d, "
-          "address=%s, attr_mask=0x%04x, sub_class=0x%02x, app_id=%d",
+      log::info(
+          "Found an existing device with the same handle dev_status={}, "
+          "address={}, attr_mask=0x{:04x}, sub_class=0x{:02x}, app_id={}",
           p_dev->dev_status, ADDRESS_TO_LOGGABLE_CSTR(p_dev->link_spec),
           p_dev->attr_mask, p_dev->sub_class, p_dev->app_id);
 
@@ -449,7 +447,7 @@
   }
 
   if (p_dev == NULL) {
-    LOG_ERROR("Too many HID devices are connected");
+    log::error("Too many HID devices are connected");
     return false;
   }
 
@@ -461,7 +459,7 @@
   CHECK(p_dev->set_rpt_id_queue);
 #endif  // ENABLE_UHID_SET_REPORT
 
-  LOG_DEBUG("Return device status %d", p_dev->dev_status);
+  log::debug("Return device status {}", p_dev->dev_status);
   return true;
 }
 
@@ -477,8 +475,9 @@
  * Returns       void.
  ******************************************************************************/
 void bta_hh_co_close(btif_hh_device_t* p_dev) {
-  LOG_INFO("Closing device handle=%d, status=%d, address=%s", p_dev->dev_handle,
-           p_dev->dev_status, ADDRESS_TO_LOGGABLE_CSTR(p_dev->link_spec));
+  log::info("Closing device handle={}, status={}, address={}",
+            p_dev->dev_handle, p_dev->dev_status,
+            ADDRESS_TO_LOGGABLE_CSTR(p_dev->link_spec));
 
   /* Clear the queues */
   fixed_queue_flush(p_dev->get_rpt_id_queue, osi_free);
@@ -521,14 +520,14 @@
                     UNUSED_ATTR const tAclLinkSpec& link_spec, uint8_t app_id) {
   btif_hh_device_t* p_dev;
 
-  LOG_VERBOSE(
-      "%s: dev_handle = %d, subclass = 0x%02X, mode = %d, "
-      "ctry_code = %d, app_id = %d",
-      __func__, dev_handle, sub_class, mode, ctry_code, app_id);
+  log::verbose(
+      "dev_handle = {}, subclass = 0x{:02X}, mode = {}, ctry_code = {}, app_id "
+      "= {}",
+      dev_handle, sub_class, mode, ctry_code, app_id);
 
   p_dev = btif_hh_find_connected_dev_by_handle(dev_handle);
   if (p_dev == NULL) {
-    LOG_WARN("%s: Error: unknown HID device handle %d", __func__, dev_handle);
+    log::warn("Error: unknown HID device handle {}", dev_handle);
     return;
   }
 
@@ -546,8 +545,8 @@
   if ((p_dev->fd >= 0) && p_dev->ready_for_data) {
     bta_hh_co_write(p_dev->fd, p_rpt, len);
   } else {
-    LOG_WARN("%s: Error: fd = %d, ready %d, len = %d", __func__, p_dev->fd,
-             p_dev->ready_for_data, len);
+    log::warn("Error: fd = {}, ready {}, len = {}", p_dev->fd,
+              p_dev->ready_for_data, len);
   }
 }
 
@@ -572,17 +571,16 @@
   struct uhid_event ev;
 
   if (p_dev->fd < 0) {
-    LOG_WARN("%s: Error: fd = %d, dscp_len = %d", __func__, p_dev->fd,
-             dscp_len);
+    log::warn("Error: fd = {}, dscp_len = {}", p_dev->fd, dscp_len);
     return;
   }
 
-  LOG_WARN("%s: fd = %d, name = [%s], dscp_len = %d", __func__, p_dev->fd,
-           dev_name, dscp_len);
-  LOG_WARN(
-      "%s: vendor_id = 0x%04x, product_id = 0x%04x, version= 0x%04x,"
-      "ctry_code=0x%02x",
-      __func__, vendor_id, product_id, version, ctry_code);
+  log::warn("fd = {}, name = [{}], dscp_len = {}", p_dev->fd, dev_name,
+            dscp_len);
+  log::warn(
+      "vendor_id = 0x{:04x}, product_id = 0x{:04x}, version= "
+      "0x{:04x},ctry_code=0x{:02x}",
+      vendor_id, product_id, version, ctry_code);
 
   // Create and send hid descriptor to kernel
   memset(&ev, 0, sizeof(ev));
@@ -608,11 +606,11 @@
   ev.u.create.country = ctry_code;
   result = uhid_write(p_dev->fd, &ev);
 
-  LOG_WARN("%s: wrote descriptor to fd = %d, dscp_len = %d, result = %d",
-           __func__, p_dev->fd, dscp_len, result);
+  log::warn("wrote descriptor to fd = {}, dscp_len = {}, result = {}",
+            p_dev->fd, dscp_len, result);
 
   if (result) {
-    LOG_WARN("%s: Error: failed to send DSCP, result = %d", __func__, result);
+    log::warn("Error: failed to send DSCP, result = {}", result);
 
     /* The HID report descriptor is corrupted. Close the driver. */
     close(p_dev->fd);
@@ -632,29 +630,29 @@
  ******************************************************************************/
 void bta_hh_co_set_rpt_rsp(uint8_t dev_handle, uint8_t status) {
 #if ENABLE_UHID_SET_REPORT
-  LOG_VERBOSE("dev_handle = %d", dev_handle);
+  log::verbose("dev_handle = {}", dev_handle);
 
   btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_handle(dev_handle);
   if (p_dev == nullptr) {
-    LOG_WARN("Unknown HID device handle %d", dev_handle);
+    log::warn("Unknown HID device handle {}", dev_handle);
     return;
   }
 
   if (!p_dev->set_rpt_id_queue) {
-    LOG_WARN("Missing UHID_SET_REPORT id queue");
+    log::warn("Missing UHID_SET_REPORT id queue");
     return;
   }
 
   // Send the HID set report reply to the kernel.
   if (p_dev->fd < 0) {
-    LOG_ERROR("Unexpected Set Report response");
+    log::error("Unexpected Set Report response");
     return;
   }
 
   uint32_t* context = (uint32_t*)fixed_queue_try_dequeue(p_dev->set_rpt_id_queue);
 
   if (context == nullptr) {
-    LOG_WARN("No pending UHID_SET_REPORT");
+    log::warn("No pending UHID_SET_REPORT");
     return;
   }
 
@@ -671,7 +669,7 @@
   osi_free(context);
 
 #else
-  LOG_ERROR("UHID_SET_REPORT_REPLY not supported");
+  log::error("UHID_SET_REPORT_REPLY not supported");
 #endif  // ENABLE_UHID_SET_REPORT
 }
 
@@ -689,34 +687,34 @@
                            const uint8_t* p_rpt, uint16_t len) {
   btif_hh_device_t* p_dev;
 
-  LOG_VERBOSE("dev_handle = %d, status = %d", dev_handle, status);
+  log::verbose("dev_handle = {}, status = {}", dev_handle, status);
 
   p_dev = btif_hh_find_connected_dev_by_handle(dev_handle);
   if (p_dev == nullptr) {
-    LOG_WARN("Unknown HID device handle %d", dev_handle);
+    log::warn("Unknown HID device handle {}", dev_handle);
     return;
   }
 
   if (!p_dev->get_rpt_id_queue) {
-    LOG_WARN("Missing UHID_GET_REPORT id queue");
+    log::warn("Missing UHID_GET_REPORT id queue");
     return;
   }
 
   // Send the HID report to the kernel.
   if (p_dev->fd < 0) {
-    LOG_WARN("Unexpected Get Report response");
+    log::warn("Unexpected Get Report response");
     return;
   }
 
   uint32_t* context = (uint32_t*)fixed_queue_try_dequeue(p_dev->get_rpt_id_queue);
 
   if (context == nullptr) {
-    LOG_WARN("No pending UHID_GET_REPORT");
+    log::warn("No pending UHID_GET_REPORT");
     return;
   }
 
   if (len == 0 || len > UHID_DATA_MAX) {
-    LOG_WARN("Invalid report size = %d", len);
+    log::warn("Invalid report size = {}", len);
     return;
   }
 
@@ -774,8 +772,8 @@
                         idx * sizeof(tBTA_HH_RPT_CACHE_ENTRY));
     btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_REPORT_VERSION,
                         BTA_HH_CACHE_REPORT_VERSION);
-    LOG_VERBOSE("%s() - Saving report; dev=%s, idx=%d", __func__,
-                ADDRESS_TO_LOGGABLE_CSTR(link_spec), idx);
+    log::verbose("Saving report; dev={}, idx={}",
+                 ADDRESS_TO_LOGGABLE_CSTR(link_spec), idx);
   }
 }
 
@@ -819,8 +817,8 @@
 
   *p_num_rpt = len / sizeof(tBTA_HH_RPT_CACHE_ENTRY);
 
-  LOG_VERBOSE("%s() - Loaded %d reports; dev=%s", __func__, *p_num_rpt,
-              ADDRESS_TO_LOGGABLE_CSTR(link_spec));
+  log::verbose("Loaded {} reports; dev={}", *p_num_rpt,
+               ADDRESS_TO_LOGGABLE_CSTR(link_spec));
 
   return sReportCache;
 }
@@ -843,6 +841,5 @@
 
   btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_REPORT);
   btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_REPORT_VERSION);
-  LOG_VERBOSE("%s() - Reset cache for bda %s", __func__,
-              ADDRESS_TO_LOGGABLE_CSTR(link_spec));
+  log::verbose("Reset cache for bda {}", ADDRESS_TO_LOGGABLE_CSTR(link_spec));
 }
diff --git a/system/btif/co/bta_pan_co.cc b/system/btif/co/bta_pan_co.cc
index dd8a0b2..5f5f2f5 100644
--- a/system/btif/co/bta_pan_co.cc
+++ b/system/btif/co/bta_pan_co.cc
@@ -31,19 +31,21 @@
 #include <hardware/bt_pan.h>
 #include <string.h>
 
-#include "os/log.h"
 #include "bta_api.h"
 #include "bta_pan_api.h"
 #include "bta_pan_ci.h"
 #include "btif_pan_internal.h"
 #include "btif_sock_thread.h"
 #include "btif_util.h"
+#include "os/log.h"
 #include "osi/include/allocator.h"
 #include "osi/include/osi.h"
 #include "pan_api.h"
 #include "stack/include/bt_hdr.h"
 #include "types/raw_address.h"
 
+using namespace bluetooth;
+
 /*******************************************************************************
  *
  * Function         bta_pan_co_init
@@ -55,7 +57,7 @@
  *
  ******************************************************************************/
 uint8_t bta_pan_co_init(uint8_t* q_level) {
-  LOG_VERBOSE("bta_pan_co_init");
+  log::verbose("bta_pan_co_init");
 
   /* set the q_level to 30 buffers */
   *q_level = 30;
@@ -76,10 +78,10 @@
  *
  ******************************************************************************/
 void bta_pan_co_close(uint16_t handle, uint8_t app_id) {
-  LOG_VERBOSE("bta_pan_co_close:app_id:%d, handle:%d", app_id, handle);
+  log::verbose("bta_pan_co_close:app_id:{}, handle:{}", app_id, handle);
   btpan_conn_t* conn = btpan_find_conn_handle(handle);
   if (conn && conn->state == PAN_STATE_OPEN) {
-    LOG_VERBOSE("bta_pan_co_close");
+    log::verbose("bta_pan_co_close");
 
     // let bta close event reset this handle as it needs
     // the handle to find the connection upon CLOSE
@@ -119,15 +121,15 @@
   bool ext;
   bool forward;
 
-  LOG_VERBOSE("%s, handle:%d, app_id:%d", __func__, handle, app_id);
+  log::verbose("handle:{}, app_id:{}", handle, app_id);
 
   btpan_conn_t* conn = btpan_find_conn_handle(handle);
   if (!conn) {
-    LOG_ERROR("%s: cannot find pan connection", __func__);
+    log::error("cannot find pan connection");
     return;
   } else if (conn->state != PAN_STATE_OPEN) {
-    LOG_ERROR("%s: conn is not opened, conn:%p, conn->state:%d", __func__, conn,
-              conn->state);
+    log::error("conn is not opened, conn:{}, conn->state:{}", fmt::ptr(conn),
+               conn->state);
     return;
   }
 
@@ -135,14 +137,12 @@
     /* read next data buffer from pan */
     p_buf = bta_pan_ci_readbuf(handle, src, dst, &protocol, &ext, &forward);
     if (p_buf) {
-      LOG_VERBOSE(
-          "%s, calling btapp_tap_send, "
-          "p_buf->len:%d, offset:%d",
-          __func__, p_buf->len, p_buf->offset);
+      log::verbose("calling btapp_tap_send, p_buf->len:{}, offset:{}",
+                   p_buf->len, p_buf->offset);
       if (is_empty_eth_addr(conn->eth_addr) && is_valid_bt_eth_addr(src)) {
-        VLOG(1) << __func__ << " pan bt peer addr: "
-                << ADDRESS_TO_LOGGABLE_STR(conn->peer)
-                << " update its ethernet addr: " << src;
+        log::verbose("pan bt peer addr: {} update its ethernet addr: {}",
+                     ADDRESS_TO_LOGGABLE_STR(conn->peer),
+                     ADDRESS_TO_LOGGABLE_STR(src));
         conn->eth_addr = src;
       }
       btpan_tap_send(btpan_cb.tap_fd, src, dst, protocol,
@@ -168,7 +168,7 @@
  ******************************************************************************/
 void bta_pan_co_rx_path(UNUSED_ATTR uint16_t handle,
                         UNUSED_ATTR uint8_t app_id) {
-  LOG_VERBOSE("bta_pan_co_rx_path not used");
+  log::verbose("bta_pan_co_rx_path not used");
 }
 
 /*******************************************************************************
@@ -187,7 +187,7 @@
  ******************************************************************************/
 void bta_pan_co_rx_flow(UNUSED_ATTR uint16_t handle, UNUSED_ATTR uint8_t app_id,
                         UNUSED_ATTR bool enable) {
-  LOG_VERBOSE("bta_pan_co_rx_flow, enabled:%d, not used", enable);
+  log::verbose("bta_pan_co_rx_flow, enabled:{}, not used", enable);
   btpan_conn_t* conn = btpan_find_conn_handle(handle);
   if (!conn || conn->state != PAN_STATE_OPEN) return;
   btpan_set_flow_control(enable);
@@ -207,7 +207,7 @@
                           UNUSED_ATTR tBTA_PAN_STATUS result,
                           UNUSED_ATTR uint16_t len,
                           UNUSED_ATTR uint8_t* p_filters) {
-  LOG_VERBOSE("bta_pan_co_pfilt_ind");
+  log::verbose("bta_pan_co_pfilt_ind");
 }
 
 /*******************************************************************************
@@ -224,5 +224,5 @@
                           UNUSED_ATTR tBTA_PAN_STATUS result,
                           UNUSED_ATTR uint16_t len,
                           UNUSED_ATTR uint8_t* p_filters) {
-  LOG_VERBOSE("bta_pan_co_mfilt_ind");
+  log::verbose("bta_pan_co_mfilt_ind");
 }
diff --git a/system/device/Android.bp b/system/device/Android.bp
index 4233340..313ce17 100644
--- a/system/device/Android.bp
+++ b/system/device/Android.bp
@@ -59,6 +59,7 @@
         "test/interop_test.cc",
     ],
     shared_libs: [
+        "libbase",
         "liblog",
     ],
     static_libs: [
@@ -95,6 +96,7 @@
         "test/device_iot_config_test.cc",
     ],
     shared_libs: [
+        "libbase",
         "libdl",
         "liblog",
     ],
diff --git a/system/device/fuzzer/Android.bp b/system/device/fuzzer/Android.bp
index 8e88d1e..a0ef51b 100644
--- a/system/device/fuzzer/Android.bp
+++ b/system/device/fuzzer/Android.bp
@@ -34,6 +34,7 @@
         "libbluetooth_headers",
     ],
     shared_libs: [
+        "libbase",
         "libdl",
         "liblog",
         "server_configurable_flags",
diff --git a/system/gd/hal/Android.bp b/system/gd/hal/Android.bp
index 27e6b6f..c583cab 100644
--- a/system/gd/hal/Android.bp
+++ b/system/gd/hal/Android.bp
@@ -10,7 +10,7 @@
 filegroup {
     name: "BluetoothHalSources",
     srcs: [
-        "nocp_iso_clocker.cc",
+        "link_clocker.cc",
         "snoop_logger.cc",
         "snoop_logger_socket.cc",
         "snoop_logger_socket_thread.cc",
diff --git a/system/gd/hal/BUILD.gn b/system/gd/hal/BUILD.gn
index 67034e1..affd151 100644
--- a/system/gd/hal/BUILD.gn
+++ b/system/gd/hal/BUILD.gn
@@ -16,7 +16,7 @@
 
 source_set("BluetoothHalSources") {
   sources = [
-    "nocp_iso_clocker.cc",
+    "link_clocker.cc",
     "snoop_logger.cc",
     "snoop_logger_socket.cc",
     "snoop_logger_socket_thread.cc",
diff --git a/system/gd/hal/hci_hal_android_hidl.cc b/system/gd/hal/hci_hal_android_hidl.cc
index 686b951..daef433 100644
--- a/system/gd/hal/hci_hal_android_hidl.cc
+++ b/system/gd/hal/hci_hal_android_hidl.cc
@@ -38,7 +38,7 @@
 #include "common/stop_watch.h"
 #include "common/strings.h"
 #include "hal/hci_hal.h"
-#include "hal/nocp_iso_clocker.h"
+#include "hal/link_clocker.h"
 #include "hal/snoop_logger.h"
 #include "os/alarm.h"
 #include "os/log.h"
@@ -83,8 +83,8 @@
 
 class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 {
  public:
-  InternalHciCallbacks(SnoopLogger* btsnoop_logger, NocpIsoClocker* nocp_iso_clocker)
-      : btsnoop_logger_(btsnoop_logger), nocp_iso_clocker_(nocp_iso_clocker) {
+  InternalHciCallbacks(SnoopLogger* btsnoop_logger, LinkClocker* link_clocker)
+      : btsnoop_logger_(btsnoop_logger), link_clocker_(link_clocker) {
     init_promise_ = new std::promise<void>();
   }
 
@@ -115,7 +115,7 @@
   Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) override {
     common::StopWatch stop_watch(GetTimerText(__func__, event));
     std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
-    nocp_iso_clocker_->OnHciEvent(received_hci_packet);
+    link_clocker_->OnHciEvent(received_hci_packet);
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
     {
@@ -130,6 +130,7 @@
   Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) override {
     common::StopWatch stop_watch(GetTimerText(__func__, data));
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
+    link_clocker_->OnAclDataReceived(received_hci_packet);
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
     {
@@ -174,7 +175,7 @@
   std::promise<void>* init_promise_ = nullptr;
   HciHalCallbacks* callback_ = nullptr;
   SnoopLogger* btsnoop_logger_ = nullptr;
-  NocpIsoClocker* nocp_iso_clocker_ = nullptr;
+  LinkClocker* link_clocker_ = nullptr;
 };
 
 static constexpr char kBluetoothAidlHalServiceName[] =
@@ -182,8 +183,8 @@
 
 class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoothHciCallbacks {
  public:
-  AidlHciCallbacks(SnoopLogger* btsnoop_logger, NocpIsoClocker* nocp_iso_clocker)
-      : btsnoop_logger_(btsnoop_logger), nocp_iso_clocker_(nocp_iso_clocker) {
+  AidlHciCallbacks(SnoopLogger* btsnoop_logger, LinkClocker* link_clocker)
+      : btsnoop_logger_(btsnoop_logger), link_clocker_(link_clocker) {
     init_promise_ = new std::promise<void>();
   }
 
@@ -212,7 +213,7 @@
   ::ndk::ScopedAStatus hciEventReceived(const std::vector<uint8_t>& event) override {
     common::StopWatch stop_watch(GetTimerText(__func__, event));
     std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
-    nocp_iso_clocker_->OnHciEvent(received_hci_packet);
+    link_clocker_->OnHciEvent(received_hci_packet);
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
     bool sent = false;
@@ -232,6 +233,7 @@
   ::ndk::ScopedAStatus aclDataReceived(const std::vector<uint8_t>& data) override {
     common::StopWatch stop_watch(GetTimerText(__func__, data));
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
+    link_clocker_->OnAclDataReceived(received_hci_packet);
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
     bool sent = false;
@@ -291,7 +293,7 @@
   std::promise<void>* init_promise_ = nullptr;
   HciHalCallbacks* callback_ = nullptr;
   SnoopLogger* btsnoop_logger_ = nullptr;
-  NocpIsoClocker* nocp_iso_clocker_ = nullptr;
+  LinkClocker* link_clocker_ = nullptr;
 };
 
 }  // namespace
@@ -362,7 +364,7 @@
 
  protected:
   void ListDependencies(ModuleList* list) const {
-    list->add<NocpIsoClocker>();
+    list->add<LinkClocker>();
     list->add<SnoopLogger>();
   }
 
@@ -374,7 +376,7 @@
     ASSERT(bt_hci_1_1_ == nullptr);
     ASSERT(aidl_hci_ == nullptr);
 
-    nocp_iso_clocker_ = GetDependency<NocpIsoClocker>();
+    link_clocker_ = GetDependency<LinkClocker>();
     btsnoop_logger_ = GetDependency<SnoopLogger>();
 
     if (AServiceManager_isDeclared(kBluetoothAidlHalServiceName)) {
@@ -408,7 +410,7 @@
           death_link == STATUS_OK, "Unable to set the death recipient for the Bluetooth HAL");
 
       aidl_callbacks_ =
-          ::ndk::SharedRefBase::make<AidlHciCallbacks>(btsnoop_logger_, nocp_iso_clocker_);
+          ::ndk::SharedRefBase::make<AidlHciCallbacks>(btsnoop_logger_, link_clocker_);
       aidl_hci_->initialize(aidl_callbacks_);
     }
   }
@@ -448,7 +450,7 @@
     ASSERT(bt_hci_ != nullptr);
     auto death_link = bt_hci_->linkToDeath(hci_death_recipient_, 0);
     ASSERT_LOG(death_link.isOk(), "Unable to set the death recipient for the Bluetooth HAL");
-    hidl_callbacks_ = new InternalHciCallbacks(btsnoop_logger_, nocp_iso_clocker_);
+    hidl_callbacks_ = new InternalHciCallbacks(btsnoop_logger_, link_clocker_);
 
     if (bt_hci_1_1_ != nullptr) {
       bt_hci_1_1_->initialize_1_1(hidl_callbacks_);
@@ -507,7 +509,7 @@
   std::shared_ptr<AidlHciCallbacks> aidl_callbacks_;
   ::ndk::ScopedAIBinder_DeathRecipient aidl_death_recipient_;
   SnoopLogger* btsnoop_logger_;
-  NocpIsoClocker* nocp_iso_clocker_;
+  LinkClocker* link_clocker_;
 };
 
 const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHidl(); });
diff --git a/system/gd/hal/hci_hal_host.cc b/system/gd/hal/hci_hal_host.cc
index 4c24c6b..e05fda0 100644
--- a/system/gd/hal/hci_hal_host.cc
+++ b/system/gd/hal/hci_hal_host.cc
@@ -30,8 +30,8 @@
 
 #include "common/init_flags.h"
 #include "hal/hci_hal.h"
+#include "hal/link_clocker.h"
 #include "hal/mgmt.h"
-#include "hal/nocp_iso_clocker.h"
 #include "hal/snoop_logger.h"
 #include "metrics/counter_metrics.h"
 #include "os/log.h"
@@ -285,7 +285,7 @@
 
  protected:
   void ListDependencies(ModuleList* list) const {
-    list->add<NocpIsoClocker>();
+    list->add<LinkClocker>();
     list->add<metrics::CounterMetrics>();
     list->add<SnoopLogger>();
   }
@@ -307,7 +307,7 @@
         common::Bind(&HciHalHost::incoming_packet_received, common::Unretained(this)),
         common::Bind(&HciHalHost::send_packet_ready, common::Unretained(this)));
     hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_, os::Reactor::REACT_ON_READ_ONLY);
-    nocp_iso_clocker_ = GetDependency<NocpIsoClocker>();
+    link_clocker_ = GetDependency<LinkClocker>();
     btsnoop_logger_ = GetDependency<SnoopLogger>();
     LOG_INFO("HAL opened successfully");
   }
@@ -348,7 +348,7 @@
   bluetooth::os::Reactor::Reactable* reactable_ = nullptr;
   std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
   SnoopLogger* btsnoop_logger_ = nullptr;
-  NocpIsoClocker* nocp_iso_clocker_ = nullptr;
+  LinkClocker* link_clocker_ = nullptr;
 
   void write_to_fd(HciPacket packet) {
     // TODO: replace this with new queue when it's ready
@@ -414,7 +414,7 @@
 
       HciPacket receivedHciPacket;
       receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciEvtHeaderSize + payload_size);
-      nocp_iso_clocker_->OnHciEvent(receivedHciPacket);
+      link_clocker_->OnHciEvent(receivedHciPacket);
       btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
       {
         std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
@@ -440,6 +440,7 @@
 
       HciPacket receivedHciPacket;
       receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciAclHeaderSize + payload_size);
+      link_clocker_->OnAclDataReceived(receivedHciPacket);
       btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
       {
         std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
diff --git a/system/gd/hal/link_clocker.cc b/system/gd/hal/link_clocker.cc
new file mode 100644
index 0000000..d0d09d9
--- /dev/null
+++ b/system/gd/hal/link_clocker.cc
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hal/link_clocker.h"
+
+#include <algorithm>
+
+namespace bluetooth::hal {
+
+static constexpr uint16_t kInvalidConnectionHandle = 0xFFFF;
+
+static class : public bluetooth::audio::asrc::ClockHandler {
+  void OnEvent(uint32_t, int, int) override {}
+} g_empty_handler;
+
+static std::atomic<bluetooth::audio::asrc::ClockHandler*> g_nocp_iso_handler = &g_empty_handler;
+
+static struct {
+  std::mutex mutex;
+  bluetooth::audio::asrc::ClockHandler* handler;
+  struct {
+    uint16_t connection_handle;
+    uint16_t stream_cid;
+  } links[2];
+} g_credit_ind_handler = {.handler = &g_empty_handler, .links = {{}, {}}};
+
+NocpIsoEvents::~NocpIsoEvents() {
+  g_nocp_iso_handler = &g_empty_handler;
+}
+
+void NocpIsoEvents::Bind(bluetooth::audio::asrc::ClockHandler* handler) {
+  g_nocp_iso_handler = handler;
+}
+
+L2capCreditIndEvents::~L2capCreditIndEvents() {
+  std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex);
+  g_credit_ind_handler.handler = &g_empty_handler;
+  g_credit_ind_handler.links[0].connection_handle = kInvalidConnectionHandle;
+  g_credit_ind_handler.links[1].connection_handle = kInvalidConnectionHandle;
+}
+
+void L2capCreditIndEvents::Bind(bluetooth::audio::asrc::ClockHandler* handler) {
+  std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex);
+  g_credit_ind_handler.handler = handler;
+  g_credit_ind_handler.links[0].connection_handle = kInvalidConnectionHandle;
+  g_credit_ind_handler.links[1].connection_handle = kInvalidConnectionHandle;
+}
+
+void L2capCreditIndEvents::Update(int link_id, uint16_t connection_handle, uint16_t stream_cid) {
+  std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex);
+  g_credit_ind_handler.links[link_id].connection_handle = connection_handle;
+  g_credit_ind_handler.links[link_id].stream_cid = stream_cid;
+}
+
+LinkClocker::LinkClocker() : cig_id_(-1), cis_handle_(-1) {}
+
+void LinkClocker::OnHciEvent(const HciPacket& packet) {
+  const int HCI_CMD_SET_CIG_PARAMETERS = 0x2062;
+  const int HCI_EVT_COMMAND_COMPLETE = 0x0e;
+  const int HCI_EVT_NUMBER_OF_COMPLETED_PACKETS = 0x13;
+
+  // HCI Event [Core 4.E.5.4.4]
+  // |  [0]  Event Code
+  // |  [1]  Parameter Total Length
+  // | [2+]  Parameters
+
+  if (packet.size() < 2) return;
+
+  const uint8_t* payload = packet.data() + 2;
+  size_t payload_length = std::min(size_t(packet[1]), packet.size() - 2);
+
+  switch (packet[0]) {
+      // HCI Command Complete Event [Core 4.E.7.7.14]
+      // |    [0]  Num_HCI_Command_Packets, Ignored
+      // | [1..2]  Command_Opcode, catch `HCI_LE_Set_CIG_Parameters`
+      // |   [3+]  Return Parameters
+
+    case HCI_EVT_COMMAND_COMPLETE: {
+      if (payload_length < 3) return;
+
+      int cmd_opcode = payload[1] | (payload[2] << 8);
+      if (cmd_opcode != HCI_CMD_SET_CIG_PARAMETERS) return;
+
+      const uint8_t* parameters = payload + 3;
+      size_t parameters_length = payload_length - 3;
+
+      // HCI LE Set CIG Parameters return parameters [4.E.7.8.97]
+      // |    [0]  Status, 0 when OK
+      // |    [1]  CIG_ID
+      // |    [2]  CIS_Count
+      // | [3..4]  Connection_Handle[0]
+
+      if (parameters_length < 3) return;
+
+      int status = parameters[0];
+      int cig_id = parameters[1];
+      int cis_count = parameters[2];
+
+      if (status != 0) return;
+
+      if (cig_id_ >= 0 && cis_handle_ >= 0 && cig_id_ != cig_id) {
+        LOG_WARN("Multiple groups not supported");
+        return;
+      }
+
+      cig_id_ = -1;
+      cis_handle_ = -1;
+
+      if (cis_count > 0 && parameters_length >= 5) {
+        cig_id_ = cig_id;
+        cis_handle_ = (parameters[3] | (parameters[4] << 8)) & 0xfff;
+      }
+
+      break;
+    }
+
+      // HCI Number Of Completed Packets event [Core 4.E.7.7.19]
+      // | [0]  Num_Handles
+      // | FOR each `Num_Handles` connection handles
+      // | | [0..1]  Connection_Handle, catch the CIS Handle
+      // | | [2..3]  Num_Completed_Packets
+
+    case HCI_EVT_NUMBER_OF_COMPLETED_PACKETS: {
+      if (payload_length < 1) return;
+
+      int i, num_handles = payload[0];
+      const uint8_t* item = payload + 1;
+      if (payload_length < size_t(1 + 4 * num_handles)) return;
+
+      for (i = 0; i < num_handles && ((item[0] | (item[1] << 8)) & 0xfff) != cis_handle_;
+           i++, item += 4)
+        ;
+      if (i >= num_handles) return;
+
+      auto timestamp = std::chrono::system_clock::now().time_since_epoch();
+      unsigned timestamp_us =
+          std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count();
+      int num_of_completed_packets = item[2] | (item[3] << 8);
+      (*g_nocp_iso_handler).OnEvent(timestamp_us, 0, num_of_completed_packets);
+
+      break;
+    }
+  }
+}
+
+/// Filter received L2CAP PDUs for Credit acknowledgments for the registered
+/// L2CAP channels.
+void LinkClocker::OnAclDataReceived(const HciPacket& packet) {
+  const int L2CAP_LE_U_CID = 0x0005;
+  const int L2CAP_FLOW_CONTROL_CREDIT_IND = 0x16;
+
+  // HCI ACL Data Packets [4.E.5.4.2]
+  // | [0..1]  Handle | PBF | BC
+  // | [2..3]  Data Total Length
+  // | [4+]    Data
+
+  if (packet.size() < 4) return;
+
+  uint16_t handle = packet[0] | (packet[1] << 8);
+  int packet_boundary_flag = (handle >> 12) & 0x3;
+  handle &= 0xfff;
+  uint16_t data_total_length = std::min(size_t(packet[2] | (packet[3] << 8)), packet.size() - 4);
+  const uint8_t* data = packet.data() + 4;
+
+  if (data_total_length < 4 || packet_boundary_flag == 0b01 || packet_boundary_flag == 0b11) return;
+
+  // L2CAP Signalling PDU Format [3.A.4]
+  // | [0..1]  PDU Length
+  // | [2..3]  Channel ID
+  // | [4+]    PDU
+  uint16_t pdu_length = std::min(data[0] | (data[1] << 8), data_total_length - 4);
+  uint16_t channel_id = data[2] | (data[3] << 8);
+  data += 4;
+
+  if (channel_id != L2CAP_LE_U_CID) return;
+
+  while (pdu_length >= 4) {
+    // | FOR each command in the PDU
+    // | | [0]     Command Code
+    // | | [1]     Command Identifier
+    // | | [2..3]  Data Length
+    // | | [4+]    Data
+    uint8_t command_code = data[0];
+    uint16_t data_length = std::min(data[2] | (data[3] << 8), pdu_length - 4);
+
+    if (command_code == L2CAP_FLOW_CONTROL_CREDIT_IND && data_length == 4) {
+      // | L2CAP Flow Control Credit Ind [3.A.4.24]
+      // | | [4..5]  CID
+      // | | [6..7]  Credits
+      uint16_t channel_id = data[4] | (data[5] << 8);
+      uint16_t credits = data[6] | (data[7] << 8);
+
+      auto timestamp = std::chrono::system_clock::now().time_since_epoch();
+      unsigned timestamp_us =
+          std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count();
+
+      {
+        std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex);
+        for (int link_id = 0; link_id < 2; link_id++) {
+          auto const& link = g_credit_ind_handler.links[link_id];
+          if (link.connection_handle == handle && link.stream_cid == channel_id) {
+            g_credit_ind_handler.handler->OnEvent(timestamp_us, link_id, credits);
+          }
+        }
+      }
+    }
+
+    data += data_length + 4;
+    pdu_length -= data_length + 4;
+  }
+}
+
+const ModuleFactory LinkClocker::Factory = ModuleFactory([]() { return new LinkClocker(); });
+
+}  // namespace bluetooth::hal
diff --git a/system/gd/hal/nocp_iso_clocker.h b/system/gd/hal/link_clocker.h
similarity index 61%
rename from system/gd/hal/nocp_iso_clocker.h
rename to system/gd/hal/link_clocker.h
index a04cbd1..c0a7278 100644
--- a/system/gd/hal/nocp_iso_clocker.h
+++ b/system/gd/hal/link_clocker.h
@@ -16,25 +16,35 @@
 
 #pragma once
 
+#include "audio/asrc/asrc_resampler.h"
 #include "hci_hal.h"
 #include "module.h"
 
 namespace bluetooth::hal {
 
-class NocpIsoHandler {
+class NocpIsoEvents : public bluetooth::audio::asrc::ClockSource {
  public:
-  virtual ~NocpIsoHandler() = default;
-  virtual void OnEvent(uint32_t timestamp_us, int link_id, int num_of_completed_packets) = 0;
+  NocpIsoEvents() = default;
+  ~NocpIsoEvents() override;
+
+  void Bind(bluetooth::audio::asrc::ClockHandler*) override;
 };
 
-class NocpIsoClocker : public ::bluetooth::Module {
+class L2capCreditIndEvents : public bluetooth::audio::asrc::ClockSource {
+ public:
+  L2capCreditIndEvents() {}
+  ~L2capCreditIndEvents() override;
+
+  void Bind(bluetooth::audio::asrc::ClockHandler*) override;
+  void Update(int link_id, uint16_t connection_handle, uint16_t stream_cid);
+};
+
+class LinkClocker : public ::bluetooth::Module {
  public:
   static const ModuleFactory Factory;
 
   void OnHciEvent(const HciPacket& packet);
-
-  static void Register(NocpIsoHandler* handler);
-  static void Unregister();
+  void OnAclDataReceived(const HciPacket& packet);
 
  protected:
   void ListDependencies(ModuleList*) const override{};
@@ -42,10 +52,10 @@
   void Stop() override{};
 
   std::string ToString() const override {
-    return std::string("NocpIsoClocker");
+    return std::string("LinkClocker");
   }
 
-  NocpIsoClocker();
+  LinkClocker();
 
  private:
   int cig_id_;
diff --git a/system/gd/hal/nocp_iso_clocker.cc b/system/gd/hal/nocp_iso_clocker.cc
deleted file mode 100644
index b97f515..0000000
--- a/system/gd/hal/nocp_iso_clocker.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "hal/nocp_iso_clocker.h"
-
-namespace bluetooth::hal {
-
-static class : public NocpIsoHandler {
-  void OnEvent(uint32_t, int, int) override {}
-} g_empty_handler;
-
-static std::atomic<NocpIsoHandler*> g_handler = &g_empty_handler;
-
-NocpIsoClocker::NocpIsoClocker() : cig_id_(-1), cis_handle_(-1) {}
-
-void NocpIsoClocker::OnHciEvent(const HciPacket& packet) {
-  const int HCI_CMD_SET_CIG_PARAMETERS = 0x2062;
-  const int HCI_EVT_COMMAND_COMPLETE = 0x0e;
-  const int HCI_EVT_NUMBER_OF_COMPLETED_PACKETS = 0x13;
-
-  // HCI Event [Core 4.E.5.4.4]
-  // |  [0]  Event Code
-  // |  [1]  Parameter Total Length
-  // | [2+]  Parameters
-
-  if (packet.size() < 2) return;
-
-  const uint8_t* payload = packet.data() + 2;
-  size_t payload_length = std::min(size_t(packet[1]), packet.size() - 2);
-
-  switch (packet[0]) {
-      // HCI Command Complete Event [Core 4.E.7.7.14]
-      // |    [0]  Num_HCI_Command_Packets, Ignored
-      // | [1..2]  Command_Opcode, catch `HCI_LE_Set_CIG_Parameters`
-      // |   [3+]  Return Parameters
-
-    case HCI_EVT_COMMAND_COMPLETE: {
-      if (payload_length < 3) return;
-
-      int cmd_opcode = payload[1] | (payload[2] << 8);
-      if (cmd_opcode != HCI_CMD_SET_CIG_PARAMETERS) return;
-
-      const uint8_t* parameters = payload + 3;
-      size_t parameters_length = payload_length - 3;
-
-      // HCI LE Set CIG Parameters return parameters [4.E.7.8.97]
-      // |    [0]  Status, 0 when OK
-      // |    [1]  CIG_ID
-      // |    [2]  CIS_Count
-      // | [3..4]  Connection_Handle[0]
-
-      if (parameters_length < 3) return;
-
-      int status = parameters[0];
-      int cig_id = parameters[1];
-      int cis_count = parameters[2];
-
-      if (status != 0) return;
-
-      if (cig_id_ >= 0 && cis_handle_ >= 0 && cig_id_ != cig_id) {
-        LOG_WARN("Multiple groups not supported");
-        return;
-      }
-
-      cig_id_ = -1;
-      cis_handle_ = -1;
-
-      if (cis_count > 0 && parameters_length >= 5) {
-        cig_id_ = cig_id;
-        cis_handle_ = (parameters[3] | (parameters[4] << 8)) & 0xfff;
-      }
-
-      break;
-    }
-
-      // HCI Number Of Completed Packets event [Core 4.E.7.7.19]
-      // | [0]  Num_Handles
-      // | FOR each `Num_Handles` connection handles
-      // | | [0..1]  Connection_Handle, catch the CIS Handle
-      // | | [2..3]  Num_Completed_Packets
-
-    case HCI_EVT_NUMBER_OF_COMPLETED_PACKETS: {
-      if (payload_length < 1) return;
-
-      int i, num_handles = payload[0];
-      const uint8_t* item = payload + 1;
-      if (payload_length < size_t(1 + 4 * num_handles)) return;
-
-      for (i = 0; i < num_handles && ((item[0] | (item[1] << 8)) & 0xfff) != cis_handle_;
-           i++, item += 4)
-        ;
-      if (i >= num_handles) return;
-
-      auto timestamp = std::chrono::system_clock::now().time_since_epoch();
-      unsigned timestamp_us =
-          std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count();
-      int num_of_completed_packets = item[2] | (item[3] << 8);
-      (*g_handler).OnEvent(timestamp_us, 0, num_of_completed_packets);
-
-      break;
-    }
-  }
-}
-
-void NocpIsoClocker::Register(NocpIsoHandler* handler) {
-  g_handler = handler;
-}
-void NocpIsoClocker::Unregister() {
-  g_handler = &g_empty_handler;
-}
-
-const ModuleFactory NocpIsoClocker::Factory = ModuleFactory([]() { return new NocpIsoClocker(); });
-
-}  // namespace bluetooth::hal
diff --git a/system/gd/rust/linux/stack/src/bluetooth_adv.rs b/system/gd/rust/linux/stack/src/bluetooth_adv.rs
index 427e362..0c33188 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_adv.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_adv.rs
@@ -6,11 +6,14 @@
 use bt_topshim::profiles::gatt::{AdvertisingStatus, Gatt, GattAdvCallbacks, LePhy};
 
 use itertools::Itertools;
-use log::{debug, error, warn};
+use log::{debug, error, info, warn};
 use num_traits::clamp;
-use std::collections::HashMap;
+use std::collections::{HashMap, VecDeque};
 use std::sync::{Arc, Mutex};
+use std::time::{Duration, Instant};
 use tokio::sync::mpsc::Sender;
+use tokio::task::JoinHandle;
+use tokio::time;
 
 use crate::bluetooth::{Bluetooth, IBluetooth};
 use crate::callbacks::Callbacks;
@@ -354,16 +357,9 @@
     }
 
     /// Checks if the advertisement can be upgraded to extended.
-    pub fn can_upgrade(
-        parameters: &mut AdvertisingSetParameters,
-        adv_bytes: &Vec<u8>,
-        is_le_extended_advertising_supported: bool,
-    ) -> bool {
-        if parameters.is_legacy
-            && is_le_extended_advertising_supported
-            && !AdvertiseData::validate_raw_data(true, adv_bytes)
-        {
-            log::info!("Auto upgrading advertisement to extended");
+    pub fn can_upgrade(parameters: &mut AdvertisingSetParameters, adv_bytes: &Vec<u8>) -> bool {
+        if parameters.is_legacy && !AdvertiseData::validate_raw_data(true, adv_bytes) {
+            info!("Auto upgrading advertisement to extended");
             parameters.is_legacy = false;
             return true;
         }
@@ -546,10 +542,15 @@
         is_le_ext_adv_supported: bool,
     ) {
         self.adv_manager_impl = if is_le_ext_adv_supported {
+            info!("AdvertiseManager: Selected extended advertising stack");
             Some(Box::new(AdvertiseManagerImpl::new(self.tx.clone(), gatt, adapter)))
         } else {
-            // TODO: Implement software rotation for legacy controller
-            Some(Box::new(AdvertiseManagerImpl::new(self.tx.clone(), gatt, adapter)))
+            info!("AdvertiseManager: Selected software rotation stack");
+            Some(Box::new(SoftwareRotationAdvertiseManagerImpl::new(
+                self.tx.clone(),
+                gatt,
+                adapter,
+            )))
         }
     }
 
@@ -699,6 +700,12 @@
     }
 }
 
+pub enum AdvertiserActions {
+    /// Triggers the rotation of the advertising set.
+    /// Should only be used in the software rotation stack.
+    RunRotate,
+}
+
 /// Defines all required ops for an AdvertiseManager to communicate with the upper/lower layers.
 pub(crate) trait AdvertiseManagerOps:
     IBluetoothAdvertiseManager + BtifGattAdvCallbacks
@@ -708,6 +715,9 @@
 
     /// Undoes previous suspend preparation
     fn exit_suspend(&mut self);
+
+    /// Handles advertise manager actions
+    fn handle_action(&mut self, action: AdvertiserActions);
 }
 
 impl AdvertiseManagerOps for AdvertiseManagerImpl {
@@ -748,6 +758,14 @@
 
         self.set_suspend_mode(SuspendMode::Normal);
     }
+
+    fn handle_action(&mut self, action: AdvertiserActions) {
+        match action {
+            AdvertiserActions::RunRotate => {
+                error!("Unexpected RunRotate call in hardware offloaded stack");
+            }
+        }
+    }
 }
 
 pub trait IBluetoothAdvertiseManager {
@@ -876,15 +894,9 @@
 
         let device_name = self.get_adapter_name();
         let adv_bytes = advertise_data.make_with(&device_name);
-        let is_le_extended_advertising_supported =
-            self.adapter.lock().unwrap().is_le_extended_advertising_supported();
         // TODO(b/311417973): Remove this once we have more robust /device/bluetooth APIs to control extended advertising
-        let is_legacy = parameters.is_legacy
-            && !AdvertiseData::can_upgrade(
-                &mut parameters,
-                &adv_bytes,
-                is_le_extended_advertising_supported,
-            );
+        let is_legacy =
+            parameters.is_legacy && !AdvertiseData::can_upgrade(&mut parameters, &adv_bytes);
         let params = parameters.into();
         if !AdvertiseData::validate_raw_data(is_legacy, &adv_bytes) {
             warn!("Failed to start advertising set with invalid advertise data");
@@ -1369,6 +1381,691 @@
     }
 }
 
+/// The underlying legacy advertising rotates every SOFTWARE_ROTATION_INTERVAL seconds.
+const SOFTWARE_ROTATION_INTERVAL: Duration = Duration::from_secs(2);
+
+/// The ID of a software rotation advertising.
+///
+/// From DBus API's perspective this is used as both Advertiser ID and Register ID.
+/// Unlike the extended advertising stack we can't propagate the LibBluetooth Advertiser ID to
+/// DBus clients because there can be at most 1 advertiser in LibBluetooth layer at the same time.
+pub type SoftwareRotationAdvertierId = i32;
+
+struct SoftwareRotationAdvertiseInfo {
+    id: SoftwareRotationAdvertierId,
+    callback_id: u32,
+
+    advertising_params: AdvertisingSetParameters,
+    advertising_data: Vec<u8>,
+    scan_response_data: Vec<u8>,
+
+    /// Filled in on the first time the advertiser started.
+    tx_power: Option<i32>,
+
+    /// True if it's advertising (from DBus client's perspective), false otherwise.
+    enabled: bool,
+    duration: i32,
+    /// None means no timeout
+    expire_time: Option<Instant>,
+}
+
+enum SoftwareRotationAdvertiseState {
+    /// No advertiser is running in LibBluetooth.
+    Stopped,
+    /// A StartAdvertisingSet call to LibBluetooth is pending.
+    Pending(SoftwareRotationAdvertierId),
+    /// An advertiser is running in LibBluetooth, i.e., an OnAdvertisingSetStarted is received.
+    /// Parameters: ID, LibBluetooth BLE Advertiser ID, rotation timer handle
+    Advertising(SoftwareRotationAdvertierId, u8, JoinHandle<()>),
+}
+
+struct SoftwareRotationAdvertiseManagerImpl {
+    callbacks: Callbacks<dyn IAdvertisingSetCallback + Send>,
+    suspend_mode: SuspendMode,
+    gatt: Arc<Mutex<Gatt>>,
+    adapter: Arc<Mutex<Box<Bluetooth>>>,
+    tx: Sender<Message>,
+
+    state: SoftwareRotationAdvertiseState,
+    adv_info: HashMap<SoftwareRotationAdvertierId, SoftwareRotationAdvertiseInfo>,
+    /// The enabled advertising sets to be rotate.
+    /// When they are removed from the queue, OnAdvertisingEnabled needs to be sent.
+    /// Note that the current advertiser running in LibBluetooth must *NOT* be in the queue.
+    adv_queue: VecDeque<SoftwareRotationAdvertierId>,
+}
+
+impl SoftwareRotationAdvertiseManagerImpl {
+    fn new(
+        tx: Sender<Message>,
+        gatt: Arc<Mutex<Gatt>>,
+        adapter: Arc<Mutex<Box<Bluetooth>>>,
+    ) -> Self {
+        Self {
+            callbacks: Callbacks::new(tx.clone(), Message::AdvertiserCallbackDisconnected),
+            suspend_mode: SuspendMode::Normal,
+            gatt,
+            adapter,
+            tx,
+            state: SoftwareRotationAdvertiseState::Stopped,
+            adv_info: HashMap::new(),
+            adv_queue: VecDeque::new(),
+        }
+    }
+}
+
+impl SoftwareRotationAdvertiseManagerImpl {
+    /// Updates suspend mode.
+    fn set_suspend_mode(&mut self, suspend_mode: SuspendMode) {
+        if suspend_mode != self.suspend_mode {
+            self.suspend_mode = suspend_mode.clone();
+            self.callbacks.for_all_callbacks(|cb| {
+                cb.on_suspend_mode_change(suspend_mode.clone());
+            });
+        }
+    }
+
+    fn get_adapter_name(&self) -> String {
+        self.adapter.lock().unwrap().get_name()
+    }
+
+    /// Returns the ID of the advertiser running in LibBluetooth.
+    fn current_id(&self) -> Option<SoftwareRotationAdvertierId> {
+        match &self.state {
+            SoftwareRotationAdvertiseState::Pending(id) => Some(*id),
+            SoftwareRotationAdvertiseState::Advertising(id, _, _) => Some(*id),
+            SoftwareRotationAdvertiseState::Stopped => None,
+        }
+    }
+
+    /// Returns the minimum unoccupied ID from 0.
+    fn new_id(&mut self) -> SoftwareRotationAdvertierId {
+        // The advertiser running in LibBluetooth may have been removed in this layer.
+        // Avoid conflicting with it.
+        let current_id = self.current_id();
+        (0..)
+            .find(|id| !self.adv_info.contains_key(id) && Some(*id) != current_id)
+            .expect("There must be an unoccupied register ID")
+    }
+
+    fn is_pending(&self) -> bool {
+        matches!(&self.state, SoftwareRotationAdvertiseState::Pending(_))
+    }
+
+    fn is_stopped(&self) -> bool {
+        matches!(&self.state, SoftwareRotationAdvertiseState::Stopped)
+    }
+
+    /// Clears the removed or disabled advertisers from the queue and invokes callback.
+    fn refresh_queue(&mut self) {
+        let now = Instant::now();
+        let adv_info = &mut self.adv_info;
+        let callbacks = &mut self.callbacks;
+        self.adv_queue.retain(|id| {
+            let Some(info) = adv_info.get_mut(id) else {
+                // This advertiser has been removed.
+                return false;
+            };
+            if info.expire_time.map_or(false, |t| t < now) {
+                // This advertiser has expired.
+                info.enabled = false;
+                callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+                    cb.on_advertising_enabled(info.id, false, AdvertisingStatus::Success);
+                });
+            }
+            info.enabled
+        });
+    }
+
+    fn stop_current_advertising(&mut self) {
+        match &self.state {
+            SoftwareRotationAdvertiseState::Advertising(id, adv_id, handle) => {
+                handle.abort();
+                self.gatt.lock().unwrap().advertiser.unregister(*adv_id);
+                self.adv_queue.push_back(*id);
+                self.state = SoftwareRotationAdvertiseState::Stopped;
+            }
+            SoftwareRotationAdvertiseState::Pending(_) => {
+                error!("stop_current_advertising: Unexpected Pending state");
+            }
+            SoftwareRotationAdvertiseState::Stopped => {}
+        };
+    }
+
+    fn start_next_advertising(&mut self) {
+        match &self.state {
+            SoftwareRotationAdvertiseState::Stopped => {
+                self.state = loop {
+                    let Some(id) = self.adv_queue.pop_front() else {
+                        break SoftwareRotationAdvertiseState::Stopped;
+                    };
+                    let Some(info) = self.adv_info.get(&id) else {
+                        error!("start_next_advertising: Unknown ID, which means queue is not refreshed!");
+                        continue;
+                    };
+                    self.gatt.lock().unwrap().advertiser.start_advertising_set(
+                        id,
+                        info.advertising_params.clone().into(),
+                        info.advertising_data.clone(),
+                        info.scan_response_data.clone(),
+                        Default::default(), // Unsupported periodic_parameters
+                        vec![],             // Unsupported periodic_data
+                        0,                  // Set no timeout. Timeout is controlled in this layer.
+                        0,                  // Unsupported max_ext_adv_events
+                    );
+                    break SoftwareRotationAdvertiseState::Pending(id);
+                }
+            }
+            SoftwareRotationAdvertiseState::Pending(_) => {
+                error!("start_next_advertising: Unexpected Pending state");
+            }
+            SoftwareRotationAdvertiseState::Advertising(_, _, _) => {
+                error!("start_next_advertising: Unexpected Advertising state");
+            }
+        };
+    }
+
+    fn run_rotate(&mut self) {
+        if self.is_pending() {
+            return;
+        }
+        let Some(current_id) = self.current_id() else {
+            // State is Stopped. Try to start next one.
+            self.refresh_queue();
+            self.start_next_advertising();
+            return;
+        };
+        // We are Advertising. Checks if the current advertiser is still allowed
+        // to advertise, or if we should schedule the next one in the queue.
+        let current_is_enabled = {
+            let now = Instant::now();
+            if let Some(info) = self.adv_info.get(&current_id) {
+                if info.enabled {
+                    info.expire_time.map_or(true, |t| t >= now)
+                } else {
+                    false
+                }
+            } else {
+                false
+            }
+        };
+        if !current_is_enabled {
+            // If current advertiser is not allowed to advertise,
+            // stop it and then let |refresh_queue| handle the callback.
+            self.stop_current_advertising();
+            self.refresh_queue();
+            self.start_next_advertising();
+        } else {
+            // Current advertiser is still enabled, refresh the other advertisers in the queue.
+            self.refresh_queue();
+            if self.adv_queue.is_empty() {
+                // No need to rotate.
+            } else {
+                self.stop_current_advertising();
+                self.start_next_advertising();
+            }
+        }
+    }
+}
+
+impl AdvertiseManagerOps for SoftwareRotationAdvertiseManagerImpl {
+    fn enter_suspend(&mut self) {
+        if self.suspend_mode != SuspendMode::Normal {
+            return;
+        }
+
+        self.set_suspend_mode(SuspendMode::Suspending);
+        if self.is_pending() {
+            // We will unregister it on_advertising_set_started and then set mode to suspended.
+            return;
+        }
+        self.stop_current_advertising();
+        self.set_suspend_mode(SuspendMode::Suspended);
+    }
+
+    fn exit_suspend(&mut self) {
+        if self.suspend_mode != SuspendMode::Suspended {
+            return;
+        }
+        self.refresh_queue();
+        self.start_next_advertising();
+        self.set_suspend_mode(SuspendMode::Normal);
+    }
+
+    fn handle_action(&mut self, action: AdvertiserActions) {
+        match action {
+            AdvertiserActions::RunRotate => {
+                if self.suspend_mode == SuspendMode::Normal {
+                    self.run_rotate();
+                }
+            }
+        }
+    }
+}
+
+/// Generates expire time from now per the definition in IBluetoothAdvertiseManager
+///
+/// None means never timeout.
+fn gen_expire_time_from_now(duration: i32) -> Option<Instant> {
+    let duration = clamp(duration, 0, 0xffff) as u64;
+    if duration != 0 {
+        Some(Instant::now() + Duration::from_millis(duration * 10))
+    } else {
+        None
+    }
+}
+
+impl IBluetoothAdvertiseManager for SoftwareRotationAdvertiseManagerImpl {
+    fn register_callback(&mut self, callback: Box<dyn IAdvertisingSetCallback + Send>) -> u32 {
+        self.callbacks.add_callback(callback)
+    }
+
+    fn unregister_callback(&mut self, callback_id: u32) -> bool {
+        self.adv_info.retain(|_, info| info.callback_id != callback_id);
+        let ret = self.callbacks.remove_callback(callback_id);
+        if let Some(current_id) = self.current_id() {
+            if !self.adv_info.contains_key(&current_id) {
+                self.run_rotate();
+            }
+        }
+        ret
+    }
+
+    fn start_advertising_set(
+        &mut self,
+        advertising_params: AdvertisingSetParameters,
+        advertising_data: AdvertiseData,
+        scan_response_data: Option<AdvertiseData>,
+        periodic_parameters: Option<PeriodicAdvertisingParameters>,
+        periodic_data: Option<AdvertiseData>,
+        duration: i32,
+        max_ext_adv_events: i32,
+        callback_id: u32,
+    ) -> i32 {
+        if self.suspend_mode != SuspendMode::Normal {
+            return INVALID_REG_ID;
+        }
+
+        let is_legacy = advertising_params.is_legacy;
+        let device_name = self.get_adapter_name();
+
+        let advertising_data = advertising_data.make_with(&device_name);
+        if !AdvertiseData::validate_raw_data(is_legacy, &advertising_data) {
+            warn!("Failed to start advertising set with invalid advertising data");
+            return INVALID_REG_ID;
+        }
+
+        let scan_response_data =
+            scan_response_data.map_or(vec![], |data| data.make_with(&device_name));
+        if !AdvertiseData::validate_raw_data(is_legacy, &scan_response_data) {
+            warn!("Failed to start advertising set with invalid scan response data");
+            return INVALID_REG_ID;
+        }
+
+        if periodic_parameters.is_some() {
+            warn!("Periodic parameters is not supported in software rotation stack, ignored");
+        }
+        if periodic_data.is_some() {
+            warn!("Periodic data is not supported in software rotation stack, ignored");
+        }
+        if max_ext_adv_events != 0 {
+            warn!("max_ext_adv_events is not supported in software rotation stack, ignored");
+        }
+
+        let id = self.new_id();
+
+        // expire_time will be determined on this advertiser is started at the first time.
+        self.adv_info.insert(
+            id,
+            SoftwareRotationAdvertiseInfo {
+                id,
+                callback_id,
+                advertising_params,
+                advertising_data,
+                scan_response_data,
+                tx_power: None,
+                enabled: true,
+                duration,
+                expire_time: None,
+            },
+        );
+        // Schedule it as the next one and rotate.
+        self.adv_queue.push_front(id);
+        self.run_rotate();
+
+        id
+    }
+
+    fn stop_advertising_set(&mut self, adv_id: i32) {
+        let current_id = self.current_id();
+        let Some(info) = self.adv_info.remove(&adv_id) else {
+            warn!("stop_advertising_set: Unknown adv_id {}", adv_id);
+            return;
+        };
+        self.callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+            cb.on_advertising_set_stopped(info.id);
+        });
+        if current_id == Some(info.id) {
+            self.run_rotate();
+        }
+    }
+
+    fn get_own_address(&mut self, _adv_id: i32) {
+        error!("get_own_address is not supported in software rotation stack");
+    }
+
+    fn enable_advertising_set(
+        &mut self,
+        adv_id: i32,
+        enable: bool,
+        duration: i32,
+        max_ext_adv_events: i32,
+    ) {
+        if self.suspend_mode != SuspendMode::Normal {
+            return;
+        }
+
+        let current_id = self.current_id();
+        let Some(info) = self.adv_info.get_mut(&adv_id) else {
+            warn!("enable_advertising_set: Unknown adv_id {}", adv_id);
+            return;
+        };
+
+        if max_ext_adv_events != 0 {
+            warn!("max_ext_adv_events is not supported in software rotation stack, ignored");
+        }
+
+        info.enabled = enable;
+        // We won't really call enable() to LibBluetooth so calculate the expire time right now.
+        info.expire_time = gen_expire_time_from_now(duration);
+        // This actually won't be used as the expire_time is already determined.
+        // Still fill it in to keep the data updated.
+        info.duration = duration;
+
+        if enable && !self.adv_queue.contains(&info.id) && current_id != Some(info.id) {
+            // The adv was not enabled and not in the queue. Invoke callback and queue it.
+            self.callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+                cb.on_advertising_enabled(info.id, false, AdvertisingStatus::Success);
+            });
+            self.adv_queue.push_back(info.id);
+            if self.is_stopped() {
+                self.start_next_advertising();
+            }
+        } else if !enable && current_id == Some(info.id) {
+            self.run_rotate();
+        }
+    }
+
+    fn set_advertising_data(&mut self, adv_id: i32, data: AdvertiseData) {
+        if self.suspend_mode != SuspendMode::Normal {
+            return;
+        }
+
+        let current_id = self.current_id();
+        let device_name = self.get_adapter_name();
+        let Some(info) = self.adv_info.get_mut(&adv_id) else {
+            warn!("set_advertising_data: Unknown adv_id {}", adv_id);
+            return;
+        };
+        let data = data.make_with(&device_name);
+        if !AdvertiseData::validate_raw_data(info.advertising_params.is_legacy, &data) {
+            warn!("set_advertising_data {}: invalid advertise data to update", adv_id);
+            return;
+        }
+        info.advertising_data = data;
+        self.callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+            cb.on_advertising_data_set(info.id, AdvertisingStatus::Success);
+        });
+
+        if current_id == Some(info.id) {
+            self.run_rotate();
+        }
+    }
+
+    fn set_raw_adv_data(&mut self, adv_id: i32, data: Vec<u8>) {
+        if self.suspend_mode != SuspendMode::Normal {
+            return;
+        }
+
+        let current_id = self.current_id();
+        let Some(info) = self.adv_info.get_mut(&adv_id) else {
+            warn!("set_raw_adv_data: Unknown adv_id {}", adv_id);
+            return;
+        };
+        if !AdvertiseData::validate_raw_data(info.advertising_params.is_legacy, &data) {
+            warn!("set_raw_adv_data {}: invalid raw advertise data to update", adv_id);
+            return;
+        }
+        info.advertising_data = data;
+        self.callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+            cb.on_advertising_data_set(info.id, AdvertisingStatus::Success);
+        });
+
+        if current_id == Some(info.id) {
+            self.run_rotate();
+        }
+    }
+
+    fn set_scan_response_data(&mut self, adv_id: i32, data: AdvertiseData) {
+        if self.suspend_mode != SuspendMode::Normal {
+            return;
+        }
+
+        let current_id = self.current_id();
+        let device_name = self.get_adapter_name();
+        let Some(info) = self.adv_info.get_mut(&adv_id) else {
+            warn!("set_scan_response_data: Unknown adv_id {}", adv_id);
+            return;
+        };
+        let data = data.make_with(&device_name);
+        if !AdvertiseData::validate_raw_data(info.advertising_params.is_legacy, &data) {
+            warn!("set_scan_response_data {}: invalid scan response to update", adv_id);
+            return;
+        }
+        info.scan_response_data = data;
+        self.callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+            cb.on_scan_response_data_set(info.id, AdvertisingStatus::Success);
+        });
+
+        if current_id == Some(info.id) {
+            self.run_rotate();
+        }
+    }
+
+    fn set_advertising_parameters(&mut self, adv_id: i32, params: AdvertisingSetParameters) {
+        if self.suspend_mode != SuspendMode::Normal {
+            return;
+        }
+
+        let current_id = self.current_id();
+        let Some(info) = self.adv_info.get_mut(&adv_id) else {
+            warn!("set_advertising_parameters: Unknown adv_id {}", adv_id);
+            return;
+        };
+        info.advertising_params = params;
+        let Some(tx_power) = info.tx_power else {
+            error!("set_advertising_parameters: tx_power is None! Is this called before adv has started?");
+            return;
+        };
+        self.callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+            cb.on_advertising_parameters_updated(info.id, tx_power, AdvertisingStatus::Success);
+        });
+
+        if current_id == Some(info.id) {
+            self.run_rotate();
+        }
+    }
+
+    fn set_periodic_advertising_parameters(
+        &mut self,
+        _adv_id: i32,
+        _parameters: PeriodicAdvertisingParameters,
+    ) {
+        error!("set_periodic_advertising_parameters is not supported in software rotation stack");
+    }
+
+    fn set_periodic_advertising_data(&mut self, _adv_id: i32, _data: AdvertiseData) {
+        error!("set_periodic_advertising_data is not supported in software rotation stack");
+    }
+
+    fn set_periodic_advertising_enable(&mut self, _adv_id: i32, _enable: bool, _include_adi: bool) {
+        error!("set_periodic_advertising_enable is not supported in software rotation stack");
+    }
+}
+
+impl BtifGattAdvCallbacks for SoftwareRotationAdvertiseManagerImpl {
+    fn on_advertising_set_started(
+        &mut self,
+        reg_id: i32,
+        adv_id: u8,
+        tx_power: i8,
+        status: AdvertisingStatus,
+    ) {
+        debug!(
+            "on_advertising_set_started(): reg_id = {}, advertiser_id = {}, tx_power = {}, status = {:?}",
+            reg_id, adv_id, tx_power, status
+        );
+
+        // Unregister if it's unexpected.
+        match &self.state {
+            SoftwareRotationAdvertiseState::Pending(pending_id) if pending_id == &reg_id => {}
+            _ => {
+                error!(
+                    "Unexpected on_advertising_set_started reg_id = {}, adv_id = {}, status = {:?}",
+                    reg_id, adv_id, status
+                );
+                if status == AdvertisingStatus::Success {
+                    self.gatt.lock().unwrap().advertiser.unregister(adv_id);
+                }
+                return;
+            }
+        }
+        // Switch out from the pending state.
+        self.state = if status != AdvertisingStatus::Success {
+            warn!("on_advertising_set_started failed: reg_id = {}, status = {:?}", reg_id, status);
+            SoftwareRotationAdvertiseState::Stopped
+        } else {
+            let txl = self.tx.clone();
+            SoftwareRotationAdvertiseState::Advertising(
+                reg_id,
+                adv_id,
+                tokio::spawn(async move {
+                    loop {
+                        time::sleep(SOFTWARE_ROTATION_INTERVAL).await;
+                        let _ = txl
+                            .send(Message::AdvertiserActions(AdvertiserActions::RunRotate))
+                            .await;
+                    }
+                }),
+            )
+        };
+
+        // 1. Handle on_advertising_set_started callback if it's the first time it started
+        // 2. Stop advertising if it's removed or disabled
+        // 3. Disable or remove the advertiser if it failed
+        if let Some(info) = self.adv_info.get_mut(&reg_id) {
+            if info.tx_power.is_none() {
+                // tx_power is none means it's the first time this advertiser started.
+                if status != AdvertisingStatus::Success {
+                    self.callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+                        cb.on_advertising_set_started(info.id, INVALID_ADV_ID, 0, status);
+                    });
+                    self.adv_info.remove(&reg_id);
+                } else {
+                    info.tx_power = Some(tx_power.into());
+                    info.expire_time = gen_expire_time_from_now(info.duration);
+                    self.callbacks.get_by_id_mut(info.callback_id).map(|cb| {
+                        cb.on_advertising_set_started(info.id, info.id, tx_power.into(), status);
+                    });
+                }
+            } else {
+                // Not the first time. This means we are not able to report the failure through
+                // on_advertising_set_started if it failed. Disable it instead in that case.
+                if status != AdvertisingStatus::Success {
+                    info.enabled = false;
+                    // Push to the queue and let refresh_queue handle the disabled callback.
+                    self.adv_queue.push_back(reg_id);
+                } else {
+                    if !info.enabled {
+                        self.stop_current_advertising();
+                    }
+                }
+            }
+        } else {
+            self.stop_current_advertising();
+        }
+
+        // Rotate again if the next advertiser is new. We need to consume all
+        // "first time" advertiser before suspended to make sure callbacks are sent.
+        if let Some(id) = self.adv_queue.front() {
+            if let Some(info) = self.adv_info.get(id) {
+                if info.tx_power.is_none() {
+                    self.run_rotate();
+                    return;
+                }
+            }
+        }
+
+        // We're fine to suspend since there is no advertiser pending callback.
+        if self.suspend_mode != SuspendMode::Normal {
+            self.stop_current_advertising();
+            self.set_suspend_mode(SuspendMode::Suspended);
+            return;
+        }
+
+        // If the current advertiser is stopped for some reason, schedule the next one.
+        if self.is_stopped() {
+            self.refresh_queue();
+            self.start_next_advertising();
+        }
+    }
+
+    fn on_advertising_enabled(&mut self, _adv_id: u8, _enabled: bool, _status: AdvertisingStatus) {
+        error!("Unexpected on_advertising_enabled in software rotation stack");
+    }
+
+    fn on_advertising_data_set(&mut self, _adv_id: u8, _status: AdvertisingStatus) {
+        error!("Unexpected on_advertising_data_set in software rotation stack");
+    }
+
+    fn on_scan_response_data_set(&mut self, _adv_id: u8, _status: AdvertisingStatus) {
+        error!("Unexpected on_scan_response_data_set in software rotation stack");
+    }
+
+    fn on_advertising_parameters_updated(
+        &mut self,
+        _adv_id: u8,
+        _tx_power: i8,
+        _status: AdvertisingStatus,
+    ) {
+        error!("Unexpected on_advertising_parameters_updated in software rotation stack");
+    }
+
+    fn on_periodic_advertising_parameters_updated(
+        &mut self,
+        _adv_id: u8,
+        _status: AdvertisingStatus,
+    ) {
+        error!("Unexpected on_periodic_advertising_parameters_updated in software rotation stack");
+    }
+
+    fn on_periodic_advertising_data_set(&mut self, _adv_id: u8, _status: AdvertisingStatus) {
+        error!("Unexpected on_periodic_advertising_data_set in software rotation stack");
+    }
+
+    fn on_periodic_advertising_enabled(
+        &mut self,
+        _adv_id: u8,
+        _enabled: bool,
+        _status: AdvertisingStatus,
+    ) {
+        error!("Unexpected on_periodic_advertising_enabled in software rotation stack");
+    }
+
+    fn on_own_address_read(&mut self, _adv_id: u8, _addr_type: u8, _address: RawAddress) {
+        error!("Unexpected on_own_address_read in software rotation stack");
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/system/gd/rust/linux/stack/src/bluetooth_gatt.rs b/system/gd/rust/linux/stack/src/bluetooth_gatt.rs
index d054af6..4e02cfb 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_gatt.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_gatt.rs
@@ -20,8 +20,8 @@
 use crate::async_helper::{AsyncHelper, CallbackSender};
 use crate::bluetooth::{Bluetooth, BluetoothDevice};
 use crate::bluetooth_adv::{
-    AdvertiseData, AdvertiseManager, AdvertisingSetParameters, BtifGattAdvCallbacks,
-    IAdvertisingSetCallback, PeriodicAdvertisingParameters,
+    AdvertiseData, AdvertiseManager, AdvertiserActions, AdvertisingSetParameters,
+    BtifGattAdvCallbacks, IAdvertisingSetCallback, PeriodicAdvertisingParameters,
 };
 use crate::callbacks::Callbacks;
 use crate::uuid::UuidHelper;
@@ -1881,6 +1881,10 @@
             }
         }
     }
+
+    pub fn handle_adv_action(&mut self, action: AdvertiserActions) {
+        self.adv_manager.get_impl().handle_action(action);
+    }
 }
 
 #[derive(Debug, FromPrimitive, ToPrimitive)]
diff --git a/system/gd/rust/linux/stack/src/lib.rs b/system/gd/rust/linux/stack/src/lib.rs
index e3fe650..6616b2d 100644
--- a/system/gd/rust/linux/stack/src/lib.rs
+++ b/system/gd/rust/linux/stack/src/lib.rs
@@ -35,7 +35,7 @@
     BluetoothDevice, DelayedActions, IBluetooth,
 };
 use crate::bluetooth_admin::{BluetoothAdmin, IBluetoothAdmin};
-use crate::bluetooth_adv::dispatch_le_adv_callbacks;
+use crate::bluetooth_adv::{dispatch_le_adv_callbacks, AdvertiserActions};
 use crate::bluetooth_gatt::{
     dispatch_gatt_client_callbacks, dispatch_gatt_server_callbacks, dispatch_le_scanner_callbacks,
     dispatch_le_scanner_inband_callbacks, BluetoothGatt, GattActions,
@@ -116,6 +116,7 @@
 
     // Advertising related
     AdvertiserCallbackDisconnected(u32),
+    AdvertiserActions(AdvertiserActions),
 
     SocketManagerActions(SocketActions),
     SocketManagerCallbackDisconnected(u32),
@@ -367,6 +368,10 @@
                     bluetooth_gatt.lock().unwrap().remove_adv_callback(id);
                 }
 
+                Message::AdvertiserActions(action) => {
+                    bluetooth_gatt.lock().unwrap().handle_adv_action(action);
+                }
+
                 Message::SocketManagerActions(action) => {
                     bluetooth_socketmgr.lock().unwrap().handle_actions(action);
                 }
diff --git a/system/include/hardware/bt_hh.h b/system/include/hardware/bt_hh.h
index 1be58aa..3cedd88 100644
--- a/system/include/hardware/bt_hh.h
+++ b/system/include/hardware/bt_hh.h
@@ -224,4 +224,14 @@
 } bthh_interface_t;
 __END_DECLS
 
+#if __has_include(<bluetooth/log.h>)
+#include <bluetooth/log.h>
+
+namespace fmt {
+template <> struct formatter<bthh_connection_state_t> :
+enum_formatter<bthh_connection_state_t> {};
+}  // namespace fmt
+
+#endif  // __has_include(<bluetooth/log.h>)
+
 #endif /* ANDROID_INCLUDE_BT_HH_H */
diff --git a/system/main/shim/hci_layer.cc b/system/main/shim/hci_layer.cc
index 9630a70..e14e106 100644
--- a/system/main/shim/hci_layer.cc
+++ b/system/main/shim/hci_layer.cc
@@ -62,11 +62,6 @@
 namespace {
 bool register_event_code(bluetooth::hci::EventCode event_code) {
   switch (event_code) {
-    // Inquiry
-    case bluetooth::hci::EventCode::INQUIRY_RESULT:
-    case bluetooth::hci::EventCode::INQUIRY_RESULT_WITH_RSSI:
-    case bluetooth::hci::EventCode::EXTENDED_INQUIRY_RESULT:
-
     // SCO
     case bluetooth::hci::EventCode::SYNCHRONOUS_CONNECTION_COMPLETE:
     case bluetooth::hci::EventCode::SYNCHRONOUS_CONNECTION_CHANGED:
diff --git a/system/stack/btm/btm_inq.cc b/system/stack/btm/btm_inq.cc
index 0b8e5c2..5cb52a7 100644
--- a/system/stack/btm/btm_inq.cc
+++ b/system/stack/btm/btm_inq.cc
@@ -25,8 +25,6 @@
  *
  ******************************************************************************/
 
-#include "hci_error_code.h"
-#include "neighbor_inquiry.h"
 #define LOG_TAG "bluetooth"
 
 #include <base/logging.h>
@@ -49,12 +47,15 @@
 #include "include/check.h"
 #include "internal_include/bt_target.h"
 #include "main/shim/entry.h"
+#include "main/shim/helpers.h"
 #include "main/shim/shim.h"
+#include "neighbor_inquiry.h"
 #include "os/log.h"
 #include "osi/include/allocator.h"
 #include "osi/include/osi.h"
 #include "osi/include/properties.h"
 #include "osi/include/stack_power_telemetry.h"
+#include "packet/bit_inserter.h"
 #include "stack/btm/btm_int_types.h"
 #include "stack/btm/btm_sec.h"
 #include "stack/include/acl_api_types.h"
@@ -65,6 +66,8 @@
 #include "stack/include/btm_api.h"
 #include "stack/include/btm_ble_api.h"
 #include "stack/include/btm_log_history.h"
+#include "stack/include/hci_error_code.h"
+#include "stack/include/hcidefs.h"
 #include "stack/include/hcimsgs.h"
 #include "stack/include/inq_hci_link_interface.h"
 #include "stack/include/main_thread.h"
@@ -689,6 +692,21 @@
         get_main_thread()->Bind([](bluetooth::hci::EventView event) {
           on_incoming_hci_event(event);
         }));
+    bluetooth::shim::GetHciLayer()->RegisterEventHandler(
+        bluetooth::hci::EventCode::INQUIRY_RESULT,
+        get_main_thread()->Bind([](bluetooth::hci::EventView event) {
+          on_incoming_hci_event(event);
+        }));
+    bluetooth::shim::GetHciLayer()->RegisterEventHandler(
+        bluetooth::hci::EventCode::INQUIRY_RESULT_WITH_RSSI,
+        get_main_thread()->Bind([](bluetooth::hci::EventView event) {
+          on_incoming_hci_event(event);
+        }));
+    bluetooth::shim::GetHciLayer()->RegisterEventHandler(
+        bluetooth::hci::EventCode::EXTENDED_INQUIRY_RESULT,
+        get_main_thread()->Bind([](bluetooth::hci::EventView event) {
+          on_incoming_hci_event(event);
+        }));
 
     btm_cb.btm_inq_vars.registered_for_hci_events = true;
   }
@@ -1320,9 +1338,7 @@
  * Returns          void
  *
  ******************************************************************************/
-static void btm_process_inq_results_standard(const uint8_t* p,
-                                             uint8_t hci_evt_len) {
-  uint8_t num_resp, xx;
+static void btm_process_inq_results_standard(bluetooth::hci::EventView event) {
   RawAddress bda;
   tINQ_DB_ENT* p_i;
   tBTM_INQ_RESULTS* p_cur = NULL;
@@ -1344,27 +1360,24 @@
     return;
   }
 
-  STREAM_TO_UINT8(num_resp, p);
+  auto standard_view = bluetooth::hci::InquiryResultView::Create(event);
+  ASSERT(standard_view.IsValid());
+  auto responses = standard_view.GetResponses();
 
-  {
-    constexpr uint16_t inquiry_result_size = 14;
-    if (hci_evt_len < num_resp * inquiry_result_size) {
-      log::error("can't fit {} results in {} bytes", num_resp, hci_evt_len);
-      return;
-    }
-  }
-
-  btm_cb.neighbor.classic_inquiry.results += num_resp;
-  for (xx = 0; xx < num_resp; xx++) {
+  btm_cb.neighbor.classic_inquiry.results += responses.size();
+  for (const auto& response : responses) {
     /* Extract inquiry results */
-    STREAM_TO_BDADDR(bda, p);
-    STREAM_TO_UINT8(page_scan_rep_mode, p);
-    STREAM_TO_UINT8(page_scan_per_mode, p);
+    bda = bluetooth::ToRawAddress(response.bd_addr_);
+    page_scan_rep_mode =
+        static_cast<uint8_t>(response.page_scan_repetition_mode_);
+    page_scan_per_mode = 0;  // reserved
+    page_scan_mode = 0;      // reserved
 
-    STREAM_TO_UINT8(page_scan_mode, p);
+    dc[0] = response.class_of_device_.cod[2];
+    dc[1] = response.class_of_device_.cod[1];
+    dc[2] = response.class_of_device_.cod[0];
 
-    STREAM_TO_DEVCLASS(dc, p);
-    STREAM_TO_UINT16(clock_offset, p);
+    clock_offset = response.clock_offset_;
 
     p_i = btm_inq_db_find(bda);
 
@@ -1439,17 +1452,10 @@
  *                  from the device. It updates the inquiry database. If the
  *                  inquiry database is full, the oldest entry is discarded.
  *
- * Parameters       inq_res_mode - BTM_INQ_RESULT_STANDARD
- *                                 BTM_INQ_RESULT_WITH_RSSI
- *                                 BTM_INQ_RESULT_EXTENDED
- *
- *
  * Returns          void
  *
  ******************************************************************************/
-static void btm_process_inq_results_rssi(const uint8_t* p, uint8_t hci_evt_len,
-                                         uint8_t inq_res_mode) {
-  uint8_t num_resp, xx;
+static void btm_process_inq_results_rssi(bluetooth::hci::EventView event) {
   RawAddress bda;
   tINQ_DB_ENT* p_i;
   tBTM_INQ_RESULTS* p_cur = NULL;
@@ -1474,45 +1480,26 @@
     return;
   }
 
-  STREAM_TO_UINT8(num_resp, p);
+  auto rssi_view = bluetooth::hci::InquiryResultWithRssiView::Create(event);
+  ASSERT(rssi_view.IsValid());
+  auto responses = rssi_view.GetResponses();
 
-  if (inq_res_mode == BTM_INQ_RESULT_EXTENDED) {
-    if (num_resp > 1) {
-      log::error("extended results ({}) > 1", num_resp);
-      return;
-    }
-
-    constexpr uint16_t extended_inquiry_result_size = 254;
-    if (hci_evt_len - 1 != extended_inquiry_result_size) {
-      log::error("can't fit {} results in {} bytes", num_resp, hci_evt_len);
-      return;
-    }
-  } else if (inq_res_mode == BTM_INQ_RESULT_STANDARD ||
-             inq_res_mode == BTM_INQ_RESULT_WITH_RSSI) {
-    constexpr uint16_t inquiry_result_size = 14;
-    if (hci_evt_len < num_resp * inquiry_result_size) {
-      log::error("can't fit {} results in {} bytes", num_resp, hci_evt_len);
-      return;
-    }
-  }
-
-  btm_cb.neighbor.classic_inquiry.results += num_resp;
-  for (xx = 0; xx < num_resp; xx++) {
+  btm_cb.neighbor.classic_inquiry.results += responses.size();
+  for (const auto& response : responses) {
     update = false;
     /* Extract inquiry results */
-    STREAM_TO_BDADDR(bda, p);
-    STREAM_TO_UINT8(page_scan_rep_mode, p);
-    STREAM_TO_UINT8(page_scan_per_mode, p);
+    bda = bluetooth::ToRawAddress(response.address_);
+    page_scan_rep_mode =
+        static_cast<uint8_t>(response.page_scan_repetition_mode_);
+    page_scan_per_mode = 0;  // reserved
+    page_scan_mode = 0;      // reserved
 
-    if (inq_res_mode == BTM_INQ_RESULT_STANDARD) {
-      STREAM_TO_UINT8(page_scan_mode, p);
-    }
+    dc[0] = response.class_of_device_.cod[2];
+    dc[1] = response.class_of_device_.cod[1];
+    dc[2] = response.class_of_device_.cod[0];
 
-    STREAM_TO_DEVCLASS(dc, p);
-    STREAM_TO_UINT16(clock_offset, p);
-    if (inq_res_mode != BTM_INQ_RESULT_STANDARD) {
-      STREAM_TO_UINT8(rssi, p);
-    }
+    clock_offset = response.clock_offset_;
+    rssi = response.rssi_;
 
     p_i = btm_inq_db_find(bda);
 
@@ -1533,13 +1520,6 @@
         p_cur->rssi = i_rssi;
         update = true;
       }
-      /* If we received a second Extended Inq Event for an already */
-      /* discovered device, this is because for the first one EIR was not
-         received */
-      else if ((inq_res_mode == BTM_INQ_RESULT_EXTENDED) && (p_i)) {
-        p_cur = &p_i->inq_info.results;
-        update = true;
-      }
       /* If no update needed continue with next response (if any) */
       else
         continue;
@@ -1553,20 +1533,15 @@
     }
 
     /* If an entry for the device already exists, overwrite it ONLY if it is
-       from
-       a previous inquiry. (Ignore it if it is a duplicate response from the
-       same
-       inquiry.
+       from a previous inquiry. (Ignore it if it is a duplicate response from
+       the same inquiry.
     */
     else if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
              (p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BREDR))
       is_new = false;
 
     /* keep updating RSSI to have latest value */
-    if (inq_res_mode != BTM_INQ_RESULT_STANDARD)
-      p_i->inq_info.results.rssi = (int8_t)rssi;
-    else
-      p_i->inq_info.results.rssi = BTM_INQ_RES_IGNORE_RSSI;
+    p_i->inq_info.results.rssi = (int8_t)rssi;
 
     if (is_new) {
       /* Save the info */
@@ -1584,17 +1559,7 @@
       if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
         /* A new response was found */
         btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
-        switch (static_cast<tBTM_INQ_RESULT>(inq_res_mode)) {
-          case BTM_INQ_RESULT_STANDARD:
-          case BTM_INQ_RESULT_WITH_RSSI:
-          case BTM_INQ_RESULT_EXTENDED:
-            btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[inq_res_mode]++;
-            break;
-          case BTM_INQ_RES_IGNORE_RSSI:
-            btm_cb.btm_inq_vars.inq_cmpl_info
-                .resp_type[BTM_INQ_RESULT_STANDARD]++;
-            break;
-        }
+        btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI]++;
       }
 
       p_cur->inq_result_type |= BT_DEVICE_TYPE_BREDR;
@@ -1611,14 +1576,7 @@
     }
 
     if (is_new || update) {
-      if (inq_res_mode == BTM_INQ_RESULT_EXTENDED) {
-        memset(p_cur->eir_uuid, 0,
-               BTM_EIR_SERVICE_ARRAY_SIZE * (BTM_EIR_ARRAY_BITS / 8));
-        /* set bit map of UUID list from received EIR */
-        btm_set_eir_uuid(p, p_cur);
-        p_eir_data = p;
-      } else
-        p_eir_data = NULL;
+      p_eir_data = NULL;
 
       /* If a callback is registered, call it with the results */
       if (p_inq_results_cb) {
@@ -1639,18 +1597,10 @@
  *                  from the device. It updates the inquiry database. If the
  *                  inquiry database is full, the oldest entry is discarded.
  *
- * Parameters       inq_res_mode - BTM_INQ_RESULT_STANDARD
- *                                 BTM_INQ_RESULT_WITH_RSSI
- *                                 BTM_INQ_RESULT_EXTENDED
- *
- *
  * Returns          void
  *
  ******************************************************************************/
-static void btm_process_inq_results_extended(const uint8_t* p,
-                                             uint8_t hci_evt_len,
-                                             uint8_t inq_res_mode) {
-  uint8_t num_resp, xx;
+static void btm_process_inq_results_extended(bluetooth::hci::EventView event) {
   RawAddress bda;
   tINQ_DB_ENT* p_i;
   tBTM_INQ_RESULTS* p_cur = NULL;
@@ -1664,7 +1614,6 @@
   uint8_t rssi = 0;
   DEV_CLASS dc;
   uint16_t clock_offset;
-  const uint8_t* p_eir_data = NULL;
 
   log::debug("Received inquiry result inq_active:0x{:x} state:{}",
              btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state);
@@ -1675,45 +1624,23 @@
     return;
   }
 
-  STREAM_TO_UINT8(num_resp, p);
+  auto extended_view = bluetooth::hci::ExtendedInquiryResultView::Create(event);
+  ASSERT(extended_view.IsValid());
 
-  if (inq_res_mode == BTM_INQ_RESULT_EXTENDED) {
-    if (num_resp > 1) {
-      log::error("extended results ({}) > 1", num_resp);
-      return;
-    }
-
-    constexpr uint16_t extended_inquiry_result_size = 254;
-    if (hci_evt_len - 1 != extended_inquiry_result_size) {
-      log::error("can't fit {} results in {} bytes", num_resp, hci_evt_len);
-      return;
-    }
-  } else if (inq_res_mode == BTM_INQ_RESULT_STANDARD ||
-             inq_res_mode == BTM_INQ_RESULT_WITH_RSSI) {
-    constexpr uint16_t inquiry_result_size = 14;
-    if (hci_evt_len < num_resp * inquiry_result_size) {
-      log::error("can't fit {} results in {} bytes", num_resp, hci_evt_len);
-      return;
-    }
-  }
-
-  btm_cb.neighbor.classic_inquiry.results += num_resp;
-  for (xx = 0; xx < num_resp; xx++) {
+  btm_cb.neighbor.classic_inquiry.results++;
+  {
     update = false;
     /* Extract inquiry results */
-    STREAM_TO_BDADDR(bda, p);
-    STREAM_TO_UINT8(page_scan_rep_mode, p);
-    STREAM_TO_UINT8(page_scan_per_mode, p);
+    bda = bluetooth::ToRawAddress(extended_view.GetAddress());
+    page_scan_rep_mode =
+        static_cast<uint8_t>(extended_view.GetPageScanRepetitionMode());
+    page_scan_per_mode = 0;  // reserved
 
-    if (inq_res_mode == BTM_INQ_RESULT_STANDARD) {
-      STREAM_TO_UINT8(page_scan_mode, p);
-    }
-
-    STREAM_TO_DEVCLASS(dc, p);
-    STREAM_TO_UINT16(clock_offset, p);
-    if (inq_res_mode != BTM_INQ_RESULT_STANDARD) {
-      STREAM_TO_UINT8(rssi, p);
-    }
+    dc[0] = extended_view.GetClassOfDevice().cod[2];
+    dc[1] = extended_view.GetClassOfDevice().cod[1];
+    dc[2] = extended_view.GetClassOfDevice().cod[0];
+    clock_offset = extended_view.GetClockOffset();
+    rssi = extended_view.GetRssi();
 
     p_i = btm_inq_db_find(bda);
 
@@ -1737,13 +1664,13 @@
       /* If we received a second Extended Inq Event for an already */
       /* discovered device, this is because for the first one EIR was not
          received */
-      else if ((inq_res_mode == BTM_INQ_RESULT_EXTENDED) && (p_i)) {
+      else if (p_i) {
         p_cur = &p_i->inq_info.results;
         update = true;
       }
       /* If no update needed continue with next response (if any) */
       else
-        continue;
+        return;
     }
 
     /* If existing entry, use that, else get a new one (possibly reusing the
@@ -1764,10 +1691,7 @@
       is_new = false;
 
     /* keep updating RSSI to have latest value */
-    if (inq_res_mode != BTM_INQ_RESULT_STANDARD)
-      p_i->inq_info.results.rssi = (int8_t)rssi;
-    else
-      p_i->inq_info.results.rssi = BTM_INQ_RES_IGNORE_RSSI;
+    p_i->inq_info.results.rssi = (int8_t)rssi;
 
     if (is_new) {
       /* Save the info */
@@ -1785,17 +1709,7 @@
       if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
         /* A new response was found */
         btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
-        switch (static_cast<tBTM_INQ_RESULT>(inq_res_mode)) {
-          case BTM_INQ_RESULT_STANDARD:
-          case BTM_INQ_RESULT_WITH_RSSI:
-          case BTM_INQ_RESULT_EXTENDED:
-            btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[inq_res_mode]++;
-            break;
-          case BTM_INQ_RES_IGNORE_RSSI:
-            btm_cb.btm_inq_vars.inq_cmpl_info
-                .resp_type[BTM_INQ_RESULT_STANDARD]++;
-            break;
-        }
+        btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED]++;
       }
 
       p_cur->inq_result_type |= BT_DEVICE_TYPE_BREDR;
@@ -1812,14 +1726,27 @@
     }
 
     if (is_new || update) {
-      if (inq_res_mode == BTM_INQ_RESULT_EXTENDED) {
+      // Create a vector of EIR data and pad it with 0
+      auto data = std::vector<uint8_t>();
+      data.reserve(HCI_EXT_INQ_RESPONSE_LEN);
+      bluetooth::packet::BitInserter bi(data);
+      for (const auto& eir : extended_view.GetExtendedInquiryResponse()) {
+        if (eir.data_type_ != static_cast<bluetooth::hci::GapDataType>(0)) {
+          eir.Serialize(bi);
+        }
+      }
+      while (data.size() < HCI_EXT_INQ_RESPONSE_LEN) {
+        data.push_back(0);
+      }
+
+      const uint8_t* p_eir_data = data.data();
+
+      {
         memset(p_cur->eir_uuid, 0,
                BTM_EIR_SERVICE_ARRAY_SIZE * (BTM_EIR_ARRAY_BITS / 8));
         /* set bit map of UUID list from received EIR */
-        btm_set_eir_uuid(p, p_cur);
-        p_eir_data = p;
-      } else
-        p_eir_data = NULL;
+        btm_set_eir_uuid(p_eir_data, p_cur);
+      }
 
       /* If a callback is registered, call it with the results */
       if (p_inq_results_cb) {
@@ -1834,36 +1761,6 @@
 
 /*******************************************************************************
  *
- * Function         btm_process_inq_results
- *
- * Description      This function is called when inquiry results are received
- *                  from the device. It updates the inquiry database. If the
- *                  inquiry database is full, the oldest entry is discarded.
- *
- * Parameters       inq_res_mode - BTM_INQ_RESULT_STANDARD
- *                                 BTM_INQ_RESULT_WITH_RSSI
- *                                 BTM_INQ_RESULT_EXTENDED
- *
- * Returns          void
- *
- ******************************************************************************/
-void btm_process_inq_results(const uint8_t* p, uint8_t hci_evt_len,
-                             uint8_t inq_res_mode) {
-  switch (inq_res_mode) {
-    case BTM_INQ_RESULT_STANDARD:
-      btm_process_inq_results_standard(p, hci_evt_len);
-      break;
-    case BTM_INQ_RESULT_WITH_RSSI:
-      btm_process_inq_results_rssi(p, hci_evt_len, BTM_INQ_RESULT_WITH_RSSI);
-      break;
-    case BTM_INQ_RESULT_EXTENDED:
-      btm_process_inq_results_extended(p, hci_evt_len, BTM_INQ_RESULT_EXTENDED);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
  * Function         btm_sort_inq_result
  *
  * Description      This function is called when inquiry complete is received
@@ -2610,6 +2507,15 @@
     case bluetooth::hci::EventCode::INQUIRY_COMPLETE:
       on_inquiry_complete(event);
       break;
+    case bluetooth::hci::EventCode::INQUIRY_RESULT:
+      btm_process_inq_results_standard(event);
+      break;
+    case bluetooth::hci::EventCode::INQUIRY_RESULT_WITH_RSSI:
+      btm_process_inq_results_rssi(event);
+      break;
+    case bluetooth::hci::EventCode::EXTENDED_INQUIRY_RESULT:
+      btm_process_inq_results_extended(event);
+      break;
     default:
       log::warn("Dropping unhandled event: {}",
                 bluetooth::hci::EventCodeText(event_code));
diff --git a/system/stack/btu/btu_hcif.cc b/system/stack/btu/btu_hcif.cc
index 2201be4..3cc6872 100644
--- a/system/stack/btu/btu_hcif.cc
+++ b/system/stack/btu/btu_hcif.cc
@@ -42,7 +42,6 @@
 #include "main/shim/hci_layer.h"
 #include "os/log.h"
 #include "osi/include/allocator.h"
-#include "stack/btm/neighbor_inquiry.h"
 #include "stack/include/acl_hci_link_interface.h"
 #include "stack/include/ble_acl_interface.h"
 #include "stack/include/ble_hci_link_interface.h"
@@ -232,15 +231,6 @@
   btu_hcif_log_event_metrics(hci_evt_code, p);
 
   switch (hci_evt_code) {
-    case HCI_INQUIRY_RESULT_EVT:
-      btm_process_inq_results(p, hci_evt_len, BTM_INQ_RESULT_STANDARD);
-      break;
-    case HCI_INQUIRY_RSSI_RESULT_EVT:
-      btm_process_inq_results(p, hci_evt_len, BTM_INQ_RESULT_WITH_RSSI);
-      break;
-    case HCI_EXTENDED_INQUIRY_RESULT_EVT:
-      btm_process_inq_results(p, hci_evt_len, BTM_INQ_RESULT_EXTENDED);
-      break;
     case HCI_AUTHENTICATION_COMP_EVT:
       btu_hcif_authentication_comp_evt(p);
       break;
diff --git a/system/stack/include/hcidefs.h b/system/stack/include/hcidefs.h
index 270885e..ce79afa 100644
--- a/system/stack/include/hcidefs.h
+++ b/system/stack/include/hcidefs.h
@@ -930,7 +930,7 @@
 #define HCI_UNISOC_ACL_HIGH_PRIORITY 0xFF
 
 /* Parameter information for HCI_MTK_SET_ACL_PRIORITY */
-#define HCI_MTK_ACL_PRIORITY_PARAM_SIZE 1
+#define HCI_MTK_ACL_PRIORITY_PARAM_SIZE 4
 #define HCI_MTK_SET_ACL_PRIORITY (0xFD95 | HCI_GRP_VENDOR_SPECIFIC)
 #define HCI_MTK_ACL_NORMAL_PRIORITY 0x00
 #define HCI_MTK_ACL_HIGH_PRIORITY 0x01
diff --git a/system/stack/include/inq_hci_link_interface.h b/system/stack/include/inq_hci_link_interface.h
index 3863889..c48c652 100644
--- a/system/stack/include/inq_hci_link_interface.h
+++ b/system/stack/include/inq_hci_link_interface.h
@@ -26,9 +26,6 @@
 void btm_process_remote_name(const RawAddress* bda, const BD_NAME name,
                              uint16_t evt_len, tHCI_STATUS hci_status);
 
-void btm_process_inq_results(const uint8_t* p, uint8_t hci_evt_len,
-                             uint8_t inq_res_mode);
-
 void btm_process_inq_complete(tHCI_STATUS status, uint8_t mode);
 
 void btm_acl_process_sca_cmpl_pkt(uint8_t len, uint8_t* data);
diff --git a/system/stack/l2cap/l2c_utils.cc b/system/stack/l2cap/l2c_utils.cc
index f1e845e..0e199f0 100644
--- a/system/stack/l2cap/l2c_utils.cc
+++ b/system/stack/l2cap/l2c_utils.cc
@@ -2342,34 +2342,6 @@
 
 /*******************************************************************************
  *
- * Function         l2cu_set_acl_priority_latency_mtk
- *
- * Description      Sends a VSC to set the ACL priority and recorded latency on
- *                  Mediatek chip.
- *
- * Returns          void
- *
- ******************************************************************************/
-
-static void l2cu_set_acl_priority_latency_mtk(tL2C_LCB* p_lcb,
-                                               tL2CAP_PRIORITY priority) {
-  uint8_t vs_param;
-  if (priority == L2CAP_PRIORITY_HIGH) {
-    // priority to high, if using latency mode check preset latency
-    log::info("Set ACL priority: High Priority Mode");
-    vs_param = HCI_MTK_ACL_HIGH_PRIORITY;
-  } else {
-    // priority to normal
-    log::info("Set ACL priority: Normal Mode");
-    vs_param = HCI_MTK_ACL_NORMAL_PRIORITY;
-  }
-
-  BTM_VendorSpecificCommand(HCI_MTK_SET_ACL_PRIORITY,
-                            HCI_MTK_ACL_PRIORITY_PARAM_SIZE, &vs_param, NULL);
-}
-
-/*******************************************************************************
- *
  * Function         l2cu_set_acl_priority
  *
  * Description      Sets the transmission priority for a channel.
@@ -2413,10 +2385,6 @@
         l2cu_set_acl_priority_unisoc(p_lcb, priority);
         break;
 
-      case LMP_COMPID_MEDIATEK:
-        l2cu_set_acl_priority_latency_mtk(p_lcb, priority);
-        break;
-
       default:
         /* Not supported/required for other vendors */
         break;
@@ -2486,6 +2454,34 @@
 
 /*******************************************************************************
  *
+ * Function         l2cu_set_acl_latency_mtk
+ *
+ * Description      Sends a VSC to set the ACL latency on Mediatek chip.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+
+static void l2cu_set_acl_latency_mtk(tL2CAP_LATENCY latency) {
+  log::info("Set ACL latency: {}",
+            latency == L2CAP_LATENCY_LOW ? "Low Latancy" : "Normal Latency");
+
+  uint8_t command[HCI_MTK_ACL_PRIORITY_PARAM_SIZE];
+  uint8_t* pp = command;
+  uint8_t vs_param = latency == L2CAP_LATENCY_LOW
+                         ? HCI_MTK_ACL_HIGH_PRIORITY
+                         : HCI_MTK_ACL_NORMAL_PRIORITY;
+  UINT8_TO_STREAM(pp, vs_param);
+  UINT8_TO_STREAM(pp, 0);
+  UINT16_TO_STREAM(pp, 0);  //reserved bytes
+
+  BTM_VendorSpecificCommand(HCI_MTK_SET_ACL_PRIORITY,
+                            HCI_MTK_ACL_PRIORITY_PARAM_SIZE, command, NULL);
+}
+
+
+/*******************************************************************************
+ *
  * Function         l2cu_set_acl_latency
  *
  * Description      Sets the transmission latency for a channel.
@@ -2516,6 +2512,10 @@
         l2cu_set_acl_latency_syna(p_lcb, latency);
         break;
 
+      case LMP_COMPID_MEDIATEK:
+        l2cu_set_acl_latency_mtk(latency);
+        break;
+
       default:
         /* Not supported/required for other vendors */
         break;
diff --git a/system/test/Android.bp b/system/test/Android.bp
index c1222ef..3116df2 100644
--- a/system/test/Android.bp
+++ b/system/test/Android.bp
@@ -611,12 +611,11 @@
         "mts_defaults",
     ],
     shared_libs: [
-        "android.hardware.bluetooth.audio@2.0",
-        "android.hardware.bluetooth.audio@2.1",
         "libbase",
         "libbinder",
         "libbinder_ndk",
         "libchrome",
+        "libcutils",
         "libhidlbase",
         "liblog",
         "libutils",
@@ -624,15 +623,15 @@
     include_dirs: ["packages/modules/Bluetooth/system"],
     srcs: ["common/hal_version_manager_test.cc"],
     static_libs: [
-        "android.hardware.bluetooth@1.0",
-        "android.hardware.bluetooth@1.1",
+        "android.hardware.audio.common@5.0",
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth.audio@2.1",
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
         "bluetooth_flags_c_lib",
         "libbt-audio-hal-interface",
         "libflagtest",
     ],
-    header_libs: ["libbluetooth_headers"],
     test_suites: ["general-tests"],
     cflags: [
         "-Wall",
diff --git a/system/test/mock/mock_stack_btm_inq.cc b/system/test/mock/mock_stack_btm_inq.cc
index 9fc34e8..fbdd941 100644
--- a/system/test/mock/mock_stack_btm_inq.cc
+++ b/system/test/mock/mock_stack_btm_inq.cc
@@ -70,7 +70,6 @@
 struct btm_inq_rmt_name_failed_cancelled btm_inq_rmt_name_failed_cancelled;
 struct btm_inq_stop_on_ssp btm_inq_stop_on_ssp;
 struct btm_process_inq_complete btm_process_inq_complete;
-struct btm_process_inq_results btm_process_inq_results;
 struct btm_process_remote_name btm_process_remote_name;
 struct btm_set_eir_uuid btm_set_eir_uuid;
 struct btm_sort_inq_result btm_sort_inq_result;
@@ -259,12 +258,6 @@
   inc_func_call_count(__func__);
   test::mock::stack_btm_inq::btm_process_inq_complete(status, mode);
 }
-void btm_process_inq_results(const uint8_t* p, uint8_t hci_evt_len,
-                             uint8_t inq_res_mode) {
-  inc_func_call_count(__func__);
-  test::mock::stack_btm_inq::btm_process_inq_results(p, hci_evt_len,
-                                                     inq_res_mode);
-}
 void btm_process_remote_name(const RawAddress* bda, const BD_NAME bdn,
                              uint16_t evt_len, tHCI_STATUS hci_status) {
   inc_func_call_count(__func__);
diff --git a/system/test/mock/mock_stack_btm_inq.h b/system/test/mock/mock_stack_btm_inq.h
index ab805ab..39f62a1 100644
--- a/system/test/mock/mock_stack_btm_inq.h
+++ b/system/test/mock/mock_stack_btm_inq.h
@@ -439,20 +439,6 @@
 };
 extern struct btm_process_inq_complete btm_process_inq_complete;
 
-// Name: btm_process_inq_results
-// Params: const uint8_t* p, uint8_t hci_evt_len, uint8_t inq_res_mode
-// Return: void
-struct btm_process_inq_results {
-  std::function<void(const uint8_t* p, uint8_t hci_evt_len,
-                     uint8_t inq_res_mode)>
-      body{[](const uint8_t* /* p */, uint8_t /* hci_evt_len */,
-              uint8_t /* inq_res_mode */) {}};
-  void operator()(const uint8_t* p, uint8_t hci_evt_len, uint8_t inq_res_mode) {
-    body(p, hci_evt_len, inq_res_mode);
-  };
-};
-extern struct btm_process_inq_results btm_process_inq_results;
-
 // Name: btm_process_remote_name
 // Params: const RawAddress* bda, const BD_NAME bdn, uint16_t evt_len,
 // tHCI_STATUS hci_status Return: void
diff --git a/tools/rootcanal/Android.bp b/tools/rootcanal/Android.bp
index 910ca1b..20447c8 100644
--- a/tools/rootcanal/Android.bp
+++ b/tools/rootcanal/Android.bp
@@ -345,6 +345,7 @@
     srcs: [
         "test/async_manager_unittest.cc",
         "test/h4_parser_unittest.cc",
+        "test/invalid_packet_handler_unittest.cc",
         "test/pcap_filter_unittest.cc",
         "test/posix_socket_unittest.cc",
     ],
@@ -356,6 +357,8 @@
     ],
     shared_libs: [
         "libbase",
+        "libcrypto",
+        "libprotobuf-cpp-full",
     ],
     static_libs: [
         "libbt-rootcanal",
diff --git a/tools/rootcanal/model/controller/dual_mode_controller.cc b/tools/rootcanal/model/controller/dual_mode_controller.cc
index 84d0f94..d7f7dc2 100644
--- a/tools/rootcanal/model/controller/dual_mode_controller.cc
+++ b/tools/rootcanal/model/controller/dual_mode_controller.cc
@@ -52,6 +52,14 @@
 constexpr bool kLeApcfTransportDiscoveryDataFilterSupported = true;
 constexpr bool kLeApcfAdTypeFilterSupported = true;
 
+#define CHECK_PACKET_VIEW(view)                                              \
+  do {                                                                       \
+    if (!CheckPacketView(view, fmt::format("{}:{} - {}() invalid packet",    \
+                                           __FILE__, __LINE__, __func__))) { \
+      return;                                                                \
+    }                                                                        \
+  } while (0)
+
 void DualModeController::SetProperties(ControllerProperties properties) {
   WARNING(id_, "updating the device properties!");
   properties_ = std::move(properties);
@@ -89,6 +97,12 @@
   ASSERT(Address::FromString("3C:5A:B4:04:05:06", public_address));
   SetAddress(public_address);
 
+  // Default invalid packet handler will abort the controller
+  // when receiving an invalid packet.
+  invalid_packet_handler_ =
+      [](uint32_t, InvalidPacketReason, std::string message,
+         std::vector<uint8_t> const&) { FATAL("{}", message); };
+
   link_layer_controller_.RegisterRemoteChannel(
       [this](std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
              Phy::Type phy_type, int8_t tx_power) {
@@ -109,7 +123,8 @@
 void DualModeController::HandleAcl(
     std::shared_ptr<std::vector<uint8_t>> packet) {
   auto acl_packet = bluetooth::hci::AclView::Create(pdl::packet::slice(packet));
-  ASSERT(acl_packet.IsValid());
+  CHECK_PACKET_VIEW(acl_packet);
+
   if (loopback_mode_ == LoopbackMode::ENABLE_LOCAL) {
     uint16_t handle = acl_packet.GetHandle();
 
@@ -134,7 +149,8 @@
 void DualModeController::HandleSco(
     std::shared_ptr<std::vector<uint8_t>> packet) {
   auto sco_packet = bluetooth::hci::ScoView::Create(pdl::packet::slice(packet));
-  ASSERT(sco_packet.IsValid());
+  CHECK_PACKET_VIEW(sco_packet);
+
   if (loopback_mode_ == LoopbackMode::ENABLE_LOCAL) {
     uint16_t handle = sco_packet.GetHandle();
 
@@ -159,7 +175,7 @@
 void DualModeController::HandleIso(
     std::shared_ptr<std::vector<uint8_t>> packet) {
   auto iso = bluetooth::hci::IsoView::Create(pdl::packet::slice(packet));
-  ASSERT(iso.IsValid());
+  CHECK_PACKET_VIEW(iso);
   link_layer_controller_.HandleIso(iso);
 }
 
@@ -167,7 +183,7 @@
     std::shared_ptr<std::vector<uint8_t>> packet) {
   auto command_packet =
       bluetooth::hci::CommandView::Create(pdl::packet::slice(packet));
-  ASSERT(command_packet.IsValid());
+  CHECK_PACKET_VIEW(command_packet);
 
   OpCode op_code = command_packet.GetOpCode();
   const bool is_vendor_command = (static_cast<uint16_t>(op_code) >> 10) == 0x3f;
@@ -238,8 +254,8 @@
 }
 
 void DualModeController::RegisterInvalidPacketHandler(
-    std::function<void(uint32_t, InvalidPacketReason, std::string,
-                       std::vector<uint8_t> const&)>& handler) {
+    const std::function<void(uint32_t, InvalidPacketReason, std::string,
+                             std::vector<uint8_t> const&)>& handler) {
   INFO(id_, "updating the invalid packet handler");
   invalid_packet_handler_ = handler;
 }
@@ -291,7 +307,7 @@
 
 void DualModeController::Reset(CommandView command) {
   auto command_view = bluetooth::hci::ResetView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Reset");
 
@@ -305,7 +321,7 @@
 
 void DualModeController::ReadBufferSize(CommandView command) {
   auto command_view = bluetooth::hci::ReadBufferSizeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Buffer Size");
 
@@ -319,7 +335,7 @@
 void DualModeController::ReadFailedContactCounter(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadFailedContactCounterView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   uint16_t connection_handle = command_view.GetConnectionHandle();
   uint16_t failed_contact_counter = 0;
@@ -337,7 +353,7 @@
 void DualModeController::ResetFailedContactCounter(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadFailedContactCounterView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t connection_handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< Reset Failed Contact Counter");
@@ -353,7 +369,7 @@
 
 void DualModeController::ReadRssi(CommandView command) {
   auto command_view = bluetooth::hci::ReadRssiView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   uint16_t connection_handle = command_view.GetConnectionHandle();
   int8_t rssi = 0;
@@ -369,7 +385,7 @@
 void DualModeController::ReadEncryptionKeySize(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadEncryptionKeySizeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Encryption Key Size");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -382,7 +398,7 @@
 
 void DualModeController::HostBufferSize(CommandView command) {
   auto command_view = bluetooth::hci::HostBufferSizeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Host Buffer Size");
 
@@ -393,7 +409,7 @@
 void DualModeController::ReadLocalVersionInformation(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadLocalVersionInformationView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Local Version Information");
 
@@ -412,7 +428,7 @@
 void DualModeController::ReadRemoteVersionInformation(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadRemoteVersionInformationView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Remote Version Information");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -427,7 +443,7 @@
 
 void DualModeController::ReadBdAddr(CommandView command) {
   auto command_view = bluetooth::hci::ReadBdAddrView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read BD_ADDR");
 
@@ -438,7 +454,7 @@
 void DualModeController::ReadLocalSupportedCommands(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadLocalSupportedCommandsView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Local Supported Commands");
 
@@ -449,7 +465,7 @@
 void DualModeController::ReadLocalSupportedFeatures(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadLocalSupportedFeaturesView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Local Supported Features");
 
@@ -461,7 +477,7 @@
 void DualModeController::ReadLocalSupportedCodecsV1(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadLocalSupportedCodecsV1View::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Local Supported Codecs V1");
 
@@ -474,7 +490,7 @@
 void DualModeController::ReadLocalExtendedFeatures(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadLocalExtendedFeaturesView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint8_t page_number = command_view.GetPageNumber();
 
   DEBUG(id_, "<< Read Local Extended Features");
@@ -489,7 +505,7 @@
 void DualModeController::ReadRemoteExtendedFeatures(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadRemoteExtendedFeaturesView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Remote Extended Features");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -505,7 +521,7 @@
 
 void DualModeController::SwitchRole(CommandView command) {
   auto command_view = bluetooth::hci::SwitchRoleView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Switch Role");
   DEBUG(id_, "   bd_addr={}", command_view.GetBdAddr());
@@ -521,7 +537,7 @@
 void DualModeController::ReadRemoteSupportedFeatures(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadRemoteSupportedFeaturesView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Remote Supported Features");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -536,7 +552,7 @@
 
 void DualModeController::ReadClockOffset(CommandView command) {
   auto command_view = bluetooth::hci::ReadClockOffsetView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Clock Offset");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -554,7 +570,7 @@
 // Support is provided to satisfy PTS tester requirements.
 void DualModeController::AddScoConnection(CommandView command) {
   auto command_view = bluetooth::hci::AddScoConnectionView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Add SCO Connection");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -571,7 +587,7 @@
 void DualModeController::SetupSynchronousConnection(CommandView command) {
   auto command_view =
       bluetooth::hci::SetupSynchronousConnectionView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Setup Synchronous Connection");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -591,7 +607,7 @@
 void DualModeController::AcceptSynchronousConnection(CommandView command) {
   auto command_view =
       bluetooth::hci::AcceptSynchronousConnectionView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Accept Synchronous Connection");
   DEBUG(id_, "   bd_addr={}", command_view.GetBdAddr());
@@ -613,7 +629,7 @@
   auto command_view =
       bluetooth::hci::EnhancedSetupSynchronousConnectionView::Create(command);
   auto status = ErrorCode::SUCCESS;
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Enhanced Setup Synchronous Connection");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -760,7 +776,7 @@
   auto command_view =
       bluetooth::hci::EnhancedAcceptSynchronousConnectionView::Create(command);
   auto status = ErrorCode::SUCCESS;
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Enhanced Accept Synchronous Connection");
   DEBUG(id_, "   bd_addr={}", command_view.GetBdAddr());
@@ -904,7 +920,7 @@
 void DualModeController::RejectSynchronousConnection(CommandView command) {
   auto command_view =
       bluetooth::hci::RejectSynchronousConnectionView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Reject Synchronous Connection");
   DEBUG(id_, "   bd_addr={}", command_view.GetBdAddr());
@@ -923,7 +939,7 @@
   auto command_view =
       bluetooth::hci::ReadInquiryResponseTransmitPowerLevelView::Create(
           command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Inquiry Response Transmit Power Level");
 
@@ -935,7 +951,7 @@
 
 void DualModeController::EnhancedFlush(CommandView command) {
   auto command_view = bluetooth::hci::EnhancedFlushView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Enhanced Flush");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -955,7 +971,7 @@
 
 void DualModeController::SetEventMaskPage2(CommandView command) {
   auto command_view = bluetooth::hci::SetEventMaskPage2View::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Set Event Mask Page 2");
   DEBUG(id_, "   event_mask_page_2=0x{:x}", command_view.GetEventMaskPage2());
@@ -985,7 +1001,7 @@
 void DualModeController::WriteSimplePairingMode(CommandView command) {
   auto command_view =
       bluetooth::hci::WriteSimplePairingModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Simple Pairing Mode");
   DEBUG(id_, "   simple_pairing_mode={}",
@@ -1001,7 +1017,7 @@
 void DualModeController::ChangeConnectionPacketType(CommandView command) {
   auto command_view =
       bluetooth::hci::ChangeConnectionPacketTypeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Change Connection Packet Type");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -1018,7 +1034,7 @@
 
 void DualModeController::WriteLeHostSupport(CommandView command) {
   auto command_view = bluetooth::hci::WriteLeHostSupportView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write LE Host Support");
   DEBUG(id_, "   le_supported_host={}",
@@ -1035,7 +1051,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::WriteSecureConnectionsHostSupportView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Secure Connections Host Support");
   DEBUG(id_, "   secure_connections_host_support={}",
@@ -1052,7 +1068,7 @@
 
 void DualModeController::SetEventMask(CommandView command) {
   auto command_view = bluetooth::hci::SetEventMaskView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Set Event Mask");
   DEBUG(id_, "   event_mask=0x{:x}", command_view.GetEventMask());
@@ -1064,7 +1080,7 @@
 
 void DualModeController::ReadInquiryMode(CommandView command) {
   auto command_view = bluetooth::hci::ReadInquiryModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Inquiry Mode");
 
@@ -1076,7 +1092,7 @@
 
 void DualModeController::WriteInquiryMode(CommandView command) {
   auto command_view = bluetooth::hci::WriteInquiryModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Inquiry Mode");
   DEBUG(id_, "   inquiry_mode={}",
@@ -1090,7 +1106,7 @@
 
 void DualModeController::ReadPageScanType(CommandView command) {
   auto command_view = bluetooth::hci::ReadPageScanTypeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Page Scan Type");
 
@@ -1102,7 +1118,7 @@
 
 void DualModeController::WritePageScanType(CommandView command) {
   auto command_view = bluetooth::hci::WritePageScanTypeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Page Scan Type");
   DEBUG(id_, "   page_scan_type={}",
@@ -1114,7 +1130,7 @@
 
 void DualModeController::ReadInquiryScanType(CommandView command) {
   auto command_view = bluetooth::hci::ReadInquiryScanTypeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Inquiry Scan Type");
 
@@ -1126,7 +1142,7 @@
 
 void DualModeController::WriteInquiryScanType(CommandView command) {
   auto command_view = bluetooth::hci::WriteInquiryScanTypeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Inquiry Scan Type");
   DEBUG(id_, "   inquiry_scan_type={}",
@@ -1139,7 +1155,7 @@
 void DualModeController::ChangeConnectionLinkKey(CommandView command) {
   auto command_view =
       bluetooth::hci::ChangeConnectionLinkKeyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Change Connection Link Key");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -1153,7 +1169,7 @@
 
 void DualModeController::CentralLinkKey(CommandView command) {
   auto command_view = bluetooth::hci::CentralLinkKeyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Central Link Key");
   DEBUG(id_, "   key_flag={}",
@@ -1169,7 +1185,7 @@
 void DualModeController::WriteAuthenticationEnable(CommandView command) {
   auto command_view =
       bluetooth::hci::WriteAuthenticationEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Authentication Enable");
   DEBUG(id_, "   authentication_enable={}",
@@ -1185,7 +1201,7 @@
 void DualModeController::ReadAuthenticationEnable(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadAuthenticationEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Authentication Enable");
 
@@ -1197,7 +1213,7 @@
 
 void DualModeController::ReadClassOfDevice(CommandView command) {
   auto command_view = bluetooth::hci::ReadClassOfDeviceView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Class of Device");
 
@@ -1208,7 +1224,7 @@
 
 void DualModeController::WriteClassOfDevice(CommandView command) {
   auto command_view = bluetooth::hci::WriteClassOfDeviceView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Class of Device");
   DEBUG(id_, "   class_of_device=0x{:x}", command_view.GetClassOfDevice());
@@ -1220,7 +1236,7 @@
 
 void DualModeController::ReadPageTimeout(CommandView command) {
   auto command_view = bluetooth::hci::ReadPageTimeoutView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Page Timeout");
 
@@ -1231,7 +1247,7 @@
 
 void DualModeController::WritePageTimeout(CommandView command) {
   auto command_view = bluetooth::hci::WritePageTimeoutView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Page Timeout");
   DEBUG(id_, "   page_timeout={}", command_view.GetPageTimeout());
@@ -1243,7 +1259,7 @@
 
 void DualModeController::HoldMode(CommandView command) {
   auto command_view = bluetooth::hci::HoldModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
   uint16_t hold_mode_max_interval = command_view.GetHoldModeMaxInterval();
   uint16_t hold_mode_min_interval = command_view.GetHoldModeMinInterval();
@@ -1260,7 +1276,7 @@
 
 void DualModeController::SniffMode(CommandView command) {
   auto command_view = bluetooth::hci::SniffModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
   uint16_t sniff_max_interval = command_view.GetSniffMaxInterval();
   uint16_t sniff_min_interval = command_view.GetSniffMinInterval();
@@ -1280,7 +1296,7 @@
 
 void DualModeController::ExitSniffMode(CommandView command) {
   auto command_view = bluetooth::hci::ExitSniffModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Exit Sniff Mode");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -1294,7 +1310,7 @@
 
 void DualModeController::QosSetup(CommandView command) {
   auto command_view = bluetooth::hci::QosSetupView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
   uint8_t service_type = static_cast<uint8_t>(command_view.GetServiceType());
   uint32_t token_rate = command_view.GetTokenRate();
@@ -1315,7 +1331,7 @@
 
 void DualModeController::RoleDiscovery(CommandView command) {
   auto command_view = bluetooth::hci::RoleDiscoveryView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< Role Discovery");
@@ -1331,7 +1347,7 @@
 void DualModeController::ReadDefaultLinkPolicySettings(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadDefaultLinkPolicySettingsView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Default Link Policy Settings");
 
@@ -1344,7 +1360,7 @@
 void DualModeController::WriteDefaultLinkPolicySettings(CommandView command) {
   auto command_view =
       bluetooth::hci::WriteDefaultLinkPolicySettingsView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Default Link Policy Settings");
   DEBUG(id_, "   default_link_policy_settings=0x{:x}",
@@ -1359,7 +1375,7 @@
 
 void DualModeController::SniffSubrating(CommandView command) {
   auto command_view = bluetooth::hci::SniffSubratingView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t connection_handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< Sniff Subrating");
@@ -1371,7 +1387,7 @@
 
 void DualModeController::FlowSpecification(CommandView command) {
   auto command_view = bluetooth::hci::FlowSpecificationView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
   uint8_t flow_direction =
       static_cast<uint8_t>(command_view.GetFlowDirection());
@@ -1395,7 +1411,7 @@
 void DualModeController::ReadLinkPolicySettings(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadLinkPolicySettingsView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< Read Link Policy Settings");
@@ -1412,7 +1428,7 @@
 void DualModeController::WriteLinkPolicySettings(CommandView command) {
   auto command_view =
       bluetooth::hci::WriteLinkPolicySettingsView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
   uint16_t settings = command_view.GetLinkPolicySettings();
 
@@ -1430,7 +1446,7 @@
 void DualModeController::WriteLinkSupervisionTimeout(CommandView command) {
   auto command_view =
       bluetooth::hci::WriteLinkSupervisionTimeoutView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
   uint16_t timeout = command_view.GetLinkSupervisionTimeout();
 
@@ -1447,7 +1463,7 @@
 
 void DualModeController::ReadLocalName(CommandView command) {
   auto command_view = bluetooth::hci::ReadLocalNameView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Local Name");
 
@@ -1458,7 +1474,7 @@
 
 void DualModeController::WriteLocalName(CommandView command) {
   auto command_view = bluetooth::hci::WriteLocalNameView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Local Name");
 
@@ -1470,7 +1486,7 @@
 void DualModeController::WriteExtendedInquiryResponse(CommandView command) {
   auto command_view =
       bluetooth::hci::WriteExtendedInquiryResponseView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Extended Inquiry Response");
 
@@ -1483,7 +1499,7 @@
 
 void DualModeController::RefreshEncryptionKey(CommandView command) {
   auto command_view = bluetooth::hci::RefreshEncryptionKeyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< Refresh Encryption Key");
@@ -1498,7 +1514,7 @@
 
 void DualModeController::ReadVoiceSetting(CommandView command) {
   auto command_view = bluetooth::hci::ReadVoiceSettingView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Voice Setting");
 
@@ -1509,7 +1525,7 @@
 
 void DualModeController::WriteVoiceSetting(CommandView command) {
   auto command_view = bluetooth::hci::WriteVoiceSettingView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Voice Setting");
   DEBUG(id_, "   voice_setting=0x{:x}", command_view.GetVoiceSetting());
@@ -1523,7 +1539,7 @@
 void DualModeController::ReadNumberOfSupportedIac(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadNumberOfSupportedIacView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Number of Supported Iac");
 
@@ -1533,7 +1549,7 @@
 
 void DualModeController::ReadCurrentIacLap(CommandView command) {
   auto command_view = bluetooth::hci::ReadCurrentIacLapView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Current Iac Lap");
 
@@ -1544,7 +1560,7 @@
 
 void DualModeController::WriteCurrentIacLap(CommandView command) {
   auto command_view = bluetooth::hci::WriteCurrentIacLapView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Current Iac Lap");
 
@@ -1555,7 +1571,7 @@
 
 void DualModeController::ReadPageScanActivity(CommandView command) {
   auto command_view = bluetooth::hci::ReadPageScanActivityView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Page Scan Activity");
 
@@ -1568,7 +1584,7 @@
 void DualModeController::WritePageScanActivity(CommandView command) {
   auto command_view =
       bluetooth::hci::WritePageScanActivityView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Page Scan Activity");
 
@@ -1579,7 +1595,7 @@
 void DualModeController::ReadInquiryScanActivity(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadInquiryScanActivityView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Inquiry Scan Activity");
 
@@ -1592,7 +1608,7 @@
 void DualModeController::WriteInquiryScanActivity(CommandView command) {
   auto command_view =
       bluetooth::hci::WriteInquiryScanActivityView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Inquiry Scan Activity");
 
@@ -1602,7 +1618,7 @@
 
 void DualModeController::ReadScanEnable(CommandView command) {
   auto command_view = bluetooth::hci::ReadScanEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Scan Enable");
 
@@ -1622,7 +1638,7 @@
 
 void DualModeController::WriteScanEnable(CommandView command) {
   auto command_view = bluetooth::hci::WriteScanEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   bluetooth::hci::ScanEnable scan_enable = command_view.GetScanEnable();
   bool inquiry_scan =
       scan_enable == bluetooth::hci::ScanEnable::INQUIRY_AND_PAGE_SCAN ||
@@ -1642,7 +1658,7 @@
 
 void DualModeController::ReadTransmitPowerLevel(CommandView command) {
   auto command_view = bluetooth::hci::ReadTransmitPowerLevelView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t connection_handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< Read Transmit Power Level");
@@ -1658,7 +1674,7 @@
 
 void DualModeController::ReadEnhancedTransmitPowerLevel(CommandView command) {
   auto command_view = bluetooth::hci::ReadEnhancedTransmitPowerLevelView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t connection_handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< Read Enhanced Transmit Power Level");
@@ -1676,7 +1692,7 @@
 void DualModeController::ReadSynchronousFlowControlEnable(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadSynchronousFlowControlEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Synchronous Flow Control Enable");
 
@@ -1693,7 +1709,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::WriteSynchronousFlowControlEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   auto enabled = command_view.GetEnable() == bluetooth::hci::Enable::ENABLED;
 
   DEBUG(id_, "<< Write Synchronous Flow Control Enable");
@@ -1707,7 +1723,7 @@
 
 void DualModeController::SetEventFilter(CommandView command) {
   auto command_view = bluetooth::hci::SetEventFilterView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Set Event Filter");
   DEBUG(id_, "   filter_type={}",
@@ -1723,7 +1739,7 @@
 
 void DualModeController::Inquiry(CommandView command) {
   auto command_view = bluetooth::hci::InquiryView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   auto max_responses = command_view.GetNumResponses();
   auto length = command_view.GetInquiryLength();
 
@@ -1746,7 +1762,7 @@
 
 void DualModeController::InquiryCancel(CommandView command) {
   auto command_view = bluetooth::hci::InquiryCancelView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Inquiry Cancel");
 
@@ -1758,7 +1774,7 @@
 void DualModeController::AcceptConnectionRequest(CommandView command) {
   auto command_view =
       bluetooth::hci::AcceptConnectionRequestView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   Address bd_addr = command_view.GetBdAddr();
   bool try_role_switch =
       command_view.GetRole() ==
@@ -1777,7 +1793,7 @@
 void DualModeController::RejectConnectionRequest(CommandView command) {
   auto command_view =
       bluetooth::hci::RejectConnectionRequestView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   Address bd_addr = command_view.GetBdAddr();
   auto reason = command_view.GetReason();
 
@@ -1794,7 +1810,7 @@
 
 void DualModeController::DeleteStoredLinkKey(CommandView command) {
   auto command_view = bluetooth::hci::DeleteStoredLinkKeyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Delete Stored Link Key");
 
@@ -1804,7 +1820,7 @@
 
 void DualModeController::RemoteNameRequest(CommandView command) {
   auto command_view = bluetooth::hci::RemoteNameRequestView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   Address bd_addr = command_view.GetBdAddr();
 
   DEBUG(id_, "<< Remote Name Request");
@@ -1819,7 +1835,7 @@
 
 void DualModeController::LeSetEventMask(CommandView command) {
   auto command_view = bluetooth::hci::LeSetEventMaskView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Event Mask");
   DEBUG(id_, "   le_event_mask=0x{:x}", command_view.GetLeEventMask());
@@ -1831,7 +1847,7 @@
 
 void DualModeController::LeRequestPeerSca(CommandView command) {
   auto command_view = bluetooth::hci::LeRequestPeerScaView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t connection_handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< LE Request Peer SCA");
@@ -1863,7 +1879,7 @@
 
 void DualModeController::LeSetHostFeature(CommandView command) {
   auto command_view = bluetooth::hci::LeSetHostFeatureView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint8_t bit_number = static_cast<uint8_t>(command_view.GetBitNumber());
   uint8_t bit_value = static_cast<uint8_t>(command_view.GetBitValue());
 
@@ -1879,7 +1895,7 @@
 
 void DualModeController::LeReadBufferSizeV1(CommandView command) {
   auto command_view = bluetooth::hci::LeReadBufferSizeV1View::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Buffer Size V1");
 
@@ -1894,7 +1910,7 @@
 
 void DualModeController::LeReadBufferSizeV2(CommandView command) {
   auto command_view = bluetooth::hci::LeReadBufferSizeV2View::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Buffer Size V2");
 
@@ -1914,7 +1930,7 @@
 void DualModeController::LeSetAddressResolutionEnable(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetAddressResolutionEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Address Resolution Enable");
   DEBUG(id_, "   address_resolution_enable={}",
@@ -1933,7 +1949,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetResolvablePrivateAddressTimeoutView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Resolvable Private Address Timeout");
 
@@ -1948,7 +1964,7 @@
 void DualModeController::LeReadLocalSupportedFeatures(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadLocalSupportedFeaturesView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Local Supported Features");
 
@@ -1960,7 +1976,7 @@
 
 void DualModeController::LeSetRandomAddress(CommandView command) {
   auto command_view = bluetooth::hci::LeSetRandomAddressView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Random Address");
   DEBUG(id_, "   random_address={}", command_view.GetRandomAddress());
@@ -1974,7 +1990,7 @@
 void DualModeController::LeSetAdvertisingParameters(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetAdvertisingParametersView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Advertising Parameters");
 
@@ -1994,7 +2010,7 @@
   auto command_view =
       bluetooth::hci::LeReadAdvertisingPhysicalChannelTxPowerView::Create(
           command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Physical Channel Tx Power");
 
@@ -2006,7 +2022,7 @@
 
 void DualModeController::LeSetAdvertisingData(CommandView command) {
   auto command_view = bluetooth::hci::LeSetAdvertisingDataView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Advertising Data");
 
@@ -2019,7 +2035,7 @@
 void DualModeController::LeSetScanResponseData(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetScanResponseDataView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Scan Response Data");
 
@@ -2032,7 +2048,7 @@
 void DualModeController::LeSetAdvertisingEnable(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetAdvertisingEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Advertising Enable");
   DEBUG(id_, "   advertising_enable={}",
@@ -2046,7 +2062,7 @@
 
 void DualModeController::LeSetScanParameters(CommandView command) {
   auto command_view = bluetooth::hci::LeSetScanParametersView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Scan Parameters");
 
@@ -2060,7 +2076,7 @@
 
 void DualModeController::LeSetScanEnable(CommandView command) {
   auto command_view = bluetooth::hci::LeSetScanEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Scan Enable");
   DEBUG(id_, "   scan_enable={}",
@@ -2075,7 +2091,7 @@
 
 void DualModeController::LeCreateConnection(CommandView command) {
   auto command_view = bluetooth::hci::LeCreateConnectionView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Create Connection");
   DEBUG(id_, "   peer_address={}", command_view.GetPeerAddress());
@@ -2105,7 +2121,7 @@
 void DualModeController::LeCreateConnectionCancel(CommandView command) {
   auto command_view =
       bluetooth::hci::LeCreateConnectionCancelView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Create Connection Cancel");
 
@@ -2116,7 +2132,7 @@
 
 void DualModeController::LeConnectionUpdate(CommandView command) {
   auto command_view = bluetooth::hci::LeConnectionUpdateView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Connection Update");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -2133,7 +2149,7 @@
 
 void DualModeController::CreateConnection(CommandView command) {
   auto command_view = bluetooth::hci::CreateConnectionView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   Address bd_addr = command_view.GetBdAddr();
   uint16_t packet_type = command_view.GetPacketType();
   uint8_t page_scan_mode =
@@ -2161,7 +2177,7 @@
 void DualModeController::CreateConnectionCancel(CommandView command) {
   auto command_view =
       bluetooth::hci::CreateConnectionCancelView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   Address address = command_view.GetBdAddr();
 
   DEBUG(id_, "<< Create Connection Cancel");
@@ -2175,7 +2191,7 @@
 
 void DualModeController::Disconnect(CommandView command) {
   auto command_view = bluetooth::hci::DisconnectView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t connection_handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< Disconnect");
@@ -2196,7 +2212,7 @@
 void DualModeController::LeReadFilterAcceptListSize(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadFilterAcceptListSizeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Filter Accept List Size");
 
@@ -2208,7 +2224,7 @@
 void DualModeController::LeClearFilterAcceptList(CommandView command) {
   auto command_view =
       bluetooth::hci::LeClearFilterAcceptListView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Clear Filter Accept List");
 
@@ -2220,7 +2236,7 @@
 void DualModeController::LeAddDeviceToFilterAcceptList(CommandView command) {
   auto command_view =
       bluetooth::hci::LeAddDeviceToFilterAcceptListView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Add Device To Filter Accept List");
   DEBUG(id_, "   address={}", command_view.GetAddress());
@@ -2239,7 +2255,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeRemoveDeviceFromFilterAcceptListView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Remove Device From Filter Accept List");
   DEBUG(id_, "   address={}", command_view.GetAddress());
@@ -2256,7 +2272,7 @@
 
 void DualModeController::LeClearResolvingList(CommandView command) {
   auto command_view = bluetooth::hci::LeClearResolvingListView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Clear Resolving List");
 
@@ -2268,7 +2284,7 @@
 void DualModeController::LeReadResolvingListSize(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadResolvingListSizeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Resolving List Size");
 
@@ -2280,7 +2296,7 @@
 void DualModeController::LeReadPeerResolvableAddress(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadPeerResolvableAddressView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Peer Resolvable Address");
   DEBUG(id_, "   peer_identity_address={}",
@@ -2301,7 +2317,7 @@
 void DualModeController::LeReadLocalResolvableAddress(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadLocalResolvableAddressView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Local Resolvable Address");
   DEBUG(id_, "   peer_identity_address={}",
@@ -2322,7 +2338,7 @@
 void DualModeController::LeReadMaximumDataLength(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadMaximumDataLengthView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Maximum Data Length");
 
@@ -2337,7 +2353,7 @@
 
 void DualModeController::LeReadPhy(CommandView command) {
   auto command_view = bluetooth::hci::LeReadPhyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t connection_handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< LE Read Phy");
@@ -2353,7 +2369,7 @@
 
 void DualModeController::LeSetDefaultPhy(CommandView command) {
   auto command_view = bluetooth::hci::LeSetDefaultPhyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Default Phy");
 
@@ -2367,7 +2383,7 @@
 
 void DualModeController::LeSetPhy(CommandView command) {
   auto command_view = bluetooth::hci::LeSetPhyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Phy");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -2384,7 +2400,7 @@
 void DualModeController::LeReadSuggestedDefaultDataLength(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadSuggestedDefaultDataLengthView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Suggested Default Data Length");
 
@@ -2399,7 +2415,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeWriteSuggestedDefaultDataLengthView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Write Suggested Default Data Length");
 
@@ -2422,7 +2438,7 @@
 void DualModeController::LeAddDeviceToResolvingList(CommandView command) {
   auto command_view =
       bluetooth::hci::LeAddDeviceToResolvingListView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Add Device to Resolving List");
   DEBUG(id_, "   peer_identity_address={}",
@@ -2442,7 +2458,7 @@
 void DualModeController::LeRemoveDeviceFromResolvingList(CommandView command) {
   auto command_view =
       bluetooth::hci::LeRemoveDeviceFromResolvingListView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Remove Device from Resolving List");
   DEBUG(id_, "   peer_identity_address={}",
@@ -2463,7 +2479,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetPeriodicAdvertisingParametersView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Periodic Advertising Parameters");
   DEBUG(id_, "   advertising_handle={}", command_view.GetAdvertisingHandle());
@@ -2481,7 +2497,7 @@
 void DualModeController::LeSetPeriodicAdvertisingData(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetPeriodicAdvertisingDataView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Periodic Advertising Data");
   DEBUG(id_, "   advertising_handle={}", command_view.GetAdvertisingHandle());
@@ -2497,7 +2513,7 @@
 void DualModeController::LeSetPeriodicAdvertisingEnable(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetPeriodicAdvertisingEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Periodic Advertising Enable");
   DEBUG(id_, "   advertising_handle={}", command_view.GetAdvertisingHandle());
@@ -2514,7 +2530,7 @@
 void DualModeController::LePeriodicAdvertisingCreateSync(CommandView command) {
   auto command_view =
       bluetooth::hci::LePeriodicAdvertisingCreateSyncView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Periodic Advertising Create Sync");
   DEBUG(id_, "   advertiser_address={}", command_view.GetAdvertiserAddress());
@@ -2537,7 +2553,7 @@
   auto command_view =
       bluetooth::hci::LePeriodicAdvertisingCreateSyncCancelView::Create(
           command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Periodic Advertising Create Sync Cancel");
 
@@ -2552,7 +2568,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LePeriodicAdvertisingTerminateSyncView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Periodic Advertising Terminate Sync");
   DEBUG(id_, "   sync_handle=0x{:x}", command_view.GetSyncHandle());
@@ -2568,7 +2584,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeAddDeviceToPeriodicAdvertiserListView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Add Device to Periodic Advertiser List");
   DEBUG(id_, "   advertiser_address={}", command_view.GetAdvertiserAddress());
@@ -2589,7 +2605,7 @@
   auto command_view =
       bluetooth::hci::LeRemoveDeviceFromPeriodicAdvertiserListView::Create(
           command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Remove Device from Periodic Advertiser List");
   DEBUG(id_, "   advertiser_address={}", command_view.GetAdvertiserAddress());
@@ -2610,7 +2626,7 @@
 void DualModeController::LeClearPeriodicAdvertiserList(CommandView command) {
   auto command_view =
       bluetooth::hci::LeClearPeriodicAdvertiserListView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Clear Periodic Advertiser List");
 
@@ -2623,7 +2639,7 @@
 void DualModeController::LeReadPeriodicAdvertiserListSize(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadPeriodicAdvertiserListSizeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Periodic Advertiser List Size");
 
@@ -2636,7 +2652,7 @@
 void DualModeController::LeSetExtendedScanParameters(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetExtendedScanParametersView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Extended Scan Parameters");
 
@@ -2651,7 +2667,7 @@
 void DualModeController::LeSetExtendedScanEnable(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetExtendedScanEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Extended Scan Enable");
   DEBUG(id_, "   enable={}",
@@ -2668,7 +2684,7 @@
 void DualModeController::LeExtendedCreateConnection(CommandView command) {
   auto command_view =
       bluetooth::hci::LeExtendedCreateConnectionView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Extended Create Connection");
   DEBUG(id_, "   peer_address={}", command_view.GetPeerAddress());
@@ -2703,7 +2719,7 @@
 
 void DualModeController::LeSetPrivacyMode(CommandView command) {
   auto command_view = bluetooth::hci::LeSetPrivacyModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Privacy Mode");
   DEBUG(id_, "   peer_identity_address={}",
@@ -2723,7 +2739,7 @@
 
 void DualModeController::LeReadRemoteFeatures(CommandView command) {
   auto command_view = bluetooth::hci::LeReadRemoteFeaturesView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< LE Read Remote Features");
@@ -2738,7 +2754,7 @@
 
 void DualModeController::LeEncrypt(CommandView command) {
   auto command_view = bluetooth::hci::LeEncryptView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Encrypt");
 
@@ -2751,7 +2767,7 @@
 
 void DualModeController::LeRand(CommandView command) {
   auto command_view = bluetooth::hci::LeRandView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Rand");
 
@@ -2762,7 +2778,7 @@
 void DualModeController::LeReadSupportedStates(CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadSupportedStatesView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Supported States");
 
@@ -2775,7 +2791,7 @@
   auto command_view =
       bluetooth::hci::LeRemoteConnectionParameterRequestReplyView::Create(
           command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Remote Connection Parameters Request Reply");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -2795,7 +2811,7 @@
     CommandView command) {
   auto command_view = bluetooth::hci::
       LeRemoteConnectionParameterRequestNegativeReplyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Remote Connection Parameters Request Negative Reply");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -2813,7 +2829,7 @@
 void DualModeController::LeGetVendorCapabilities(CommandView command) {
   auto command_view =
       bluetooth::hci::LeGetVendorCapabilitiesView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   if (!properties_.supports_le_get_vendor_capabilities_command) {
     SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_GET_VENDOR_CAPABILITIES);
@@ -2843,13 +2859,13 @@
 
 void DualModeController::LeBatchScan(CommandView command) {
   auto command_view = bluetooth::hci::LeBatchScanView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_BATCH_SCAN);
 }
 
 void DualModeController::LeApcf(CommandView command) {
   auto command_view = bluetooth::hci::LeApcfView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   if (!properties_.supports_le_apcf_vendor_command) {
     SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_APCF);
@@ -2860,7 +2876,7 @@
     case bluetooth::hci::ApcfOpcode::ENABLE: {
       auto subcommand_view =
           bluetooth::hci::LeApcfEnableView::Create(command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Enable");
       DEBUG(id_, "   enable={}",
@@ -2876,7 +2892,7 @@
       auto subcommand_view =
           bluetooth::hci::LeApcfSetFilteringParametersView::Create(
               command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Set Filtering Parameters");
       DEBUG(id_, "   action={}",
@@ -2890,7 +2906,7 @@
           auto subsubcommand_view =
               bluetooth::hci::LeApcfAddFilteringParametersView::Create(
                   subcommand_view);
-          ASSERT(subsubcommand_view.IsValid());
+          CHECK_PACKET_VIEW(subcommand_view);
           status = link_layer_controller_.LeApcfAddFilteringParameters(
               subsubcommand_view.GetApcfFilterIndex(),
               subsubcommand_view.GetApcfFeatureSelection(),
@@ -2910,7 +2926,7 @@
           auto subsubcommand_view =
               bluetooth::hci::LeApcfDeleteFilteringParametersView::Create(
                   subcommand_view);
-          ASSERT(subsubcommand_view.IsValid());
+          CHECK_PACKET_VIEW(subcommand_view);
           status = link_layer_controller_.LeApcfDeleteFilteringParameters(
               subsubcommand_view.GetApcfFilterIndex(), &apcf_available_spaces);
           break;
@@ -2919,7 +2935,7 @@
           auto subsubcommand_view =
               bluetooth::hci::LeApcfClearFilteringParametersView::Create(
                   subcommand_view);
-          ASSERT(subsubcommand_view.IsValid());
+          CHECK_PACKET_VIEW(subcommand_view);
           status = link_layer_controller_.LeApcfClearFilteringParameters(
               &apcf_available_spaces);
           break;
@@ -2939,7 +2955,7 @@
     case bluetooth::hci::ApcfOpcode::BROADCASTER_ADDRESS: {
       auto subcommand_view =
           bluetooth::hci::LeApcfBroadcasterAddressView::Create(command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Broadcaster Address");
       DEBUG(id_, "   action={}",
@@ -2953,7 +2969,7 @@
           auto subsubcommand_view =
               bluetooth::hci::LeApcfAddBroadcasterAddressView::Create(
                   subcommand_view);
-          ASSERT(subsubcommand_view.IsValid());
+          CHECK_PACKET_VIEW(subcommand_view);
           status = link_layer_controller_.LeApcfBroadcasterAddress(
               bluetooth::hci::ApcfAction::ADD,
               subsubcommand_view.GetApcfFilterIndex(),
@@ -2966,7 +2982,7 @@
           auto subsubcommand_view =
               bluetooth::hci::LeApcfDeleteBroadcasterAddressView::Create(
                   subcommand_view);
-          ASSERT(subsubcommand_view.IsValid());
+          CHECK_PACKET_VIEW(subcommand_view);
           status = link_layer_controller_.LeApcfBroadcasterAddress(
               bluetooth::hci::ApcfAction::DELETE,
               subsubcommand_view.GetApcfFilterIndex(),
@@ -2979,7 +2995,7 @@
           auto subsubcommand_view =
               bluetooth::hci::LeApcfClearBroadcasterAddressView::Create(
                   subcommand_view);
-          ASSERT(subsubcommand_view.IsValid());
+          CHECK_PACKET_VIEW(subcommand_view);
           status = link_layer_controller_.LeApcfBroadcasterAddress(
               bluetooth::hci::ApcfAction::CLEAR,
               subsubcommand_view.GetApcfFilterIndex(), Address(),
@@ -3002,7 +3018,7 @@
     case bluetooth::hci::ApcfOpcode::SERVICE_UUID: {
       auto subcommand_view =
           bluetooth::hci::LeApcfServiceUuidView::Create(command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Service UUID");
       DEBUG(id_, "   action={}",
@@ -3021,7 +3037,7 @@
       auto subcommand_view =
           bluetooth::hci::LeApcfServiceSolicitationUuidView::Create(
               command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Service Solicitation UUID");
       DEBUG(id_, "   action={}",
@@ -3040,7 +3056,7 @@
     case bluetooth::hci::ApcfOpcode::LOCAL_NAME: {
       auto subcommand_view =
           bluetooth::hci::LeApcfLocalNameView::Create(command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Local Name");
       DEBUG(id_, "   action={}",
@@ -3058,7 +3074,7 @@
     case bluetooth::hci::ApcfOpcode::MANUFACTURER_DATA: {
       auto subcommand_view =
           bluetooth::hci::LeApcfManufacturerDataView::Create(command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Manufacturer Data");
       DEBUG(id_, "   action={}",
@@ -3076,7 +3092,7 @@
     case bluetooth::hci::ApcfOpcode::SERVICE_DATA: {
       auto subcommand_view =
           bluetooth::hci::LeApcfServiceDataView::Create(command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Service Data");
       DEBUG(id_, "   action={}",
@@ -3094,7 +3110,7 @@
     case bluetooth::hci::ApcfOpcode::AD_TYPE_FILTER: {
       auto subcommand_view =
           bluetooth::hci::LeApcfAdTypeFilterView::Create(command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF AD Type Filter");
       DEBUG(id_, "   action={}",
@@ -3113,7 +3129,7 @@
     case bluetooth::hci::ApcfOpcode::READ_EXTENDED_FEATURES: {
       auto subcommand_view =
           bluetooth::hci::LeApcfReadExtendedFeaturesView::Create(command_view);
-      ASSERT(subcommand_view.IsValid());
+      CHECK_PACKET_VIEW(subcommand_view);
 
       DEBUG(id_, "<< LE APCF Read Extended Features");
 
@@ -3134,7 +3150,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeGetControllerActivityEnergyInfoView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   SendCommandCompleteUnknownOpCodeEvent(
       OpCode::LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO);
 }
@@ -3142,14 +3158,14 @@
 void DualModeController::LeExSetScanParameters(CommandView command) {
   auto command_view =
       bluetooth::hci::LeExSetScanParametersView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_EX_SET_SCAN_PARAMETERS);
 }
 
 void DualModeController::GetControllerDebugInfo(CommandView command) {
   auto command_view =
       bluetooth::hci::GetControllerDebugInfoView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Get Controller Debug Info");
 
@@ -3338,7 +3354,7 @@
 void DualModeController::LeSetAdvertisingSetRandomAddress(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetAdvertisingSetRandomAddressView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Advertising Set Random Address");
   DEBUG(id_, "   advertising_handle={}", command_view.GetAdvertisingHandle());
@@ -3355,7 +3371,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetExtendedAdvertisingParametersView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Extended Advertising Parameters");
   DEBUG(id_, "   advertising_handle={}", command_view.GetAdvertisingHandle());
@@ -3384,7 +3400,7 @@
 void DualModeController::LeSetExtendedAdvertisingData(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetExtendedAdvertisingDataView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Extended Advertising Data");
   DEBUG(id_, "   advertising_handle={}", command_view.GetAdvertisingHandle());
@@ -3400,7 +3416,7 @@
 void DualModeController::LeSetExtendedScanResponseData(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetExtendedScanResponseDataView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Extended Scan Response Data");
   DEBUG(id_, "   advertising_handle={}", command_view.GetAdvertisingHandle());
@@ -3416,7 +3432,7 @@
 void DualModeController::LeSetExtendedAdvertisingEnable(CommandView command) {
   auto command_view =
       bluetooth::hci::LeSetExtendedAdvertisingEnableView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Set Extended Advertising Enable");
   DEBUG(id_, "   enable={}",
@@ -3437,7 +3453,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeReadMaximumAdvertisingDataLengthView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Maximum Advertising Data Length");
 
@@ -3452,7 +3468,7 @@
   auto command_view =
       bluetooth::hci::LeReadNumberOfSupportedAdvertisingSetsView::Create(
           command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Read Number of Supported Advertising Sets");
 
@@ -3465,7 +3481,7 @@
 void DualModeController::LeRemoveAdvertisingSet(CommandView command) {
   auto command_view =
       bluetooth::hci::LeRemoveAdvertisingSetView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Remove Advertising Set");
   DEBUG(id_, "   advertising_handle={}", command_view.GetAdvertisingHandle());
@@ -3479,7 +3495,7 @@
 void DualModeController::LeClearAdvertisingSets(CommandView command) {
   auto command_view =
       bluetooth::hci::LeClearAdvertisingSetsView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Clear Advertising Sets");
 
@@ -3490,7 +3506,7 @@
 
 void DualModeController::LeStartEncryption(CommandView command) {
   auto command_view = bluetooth::hci::LeStartEncryptionView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< LE Start Encryption");
   DEBUG(id_, "   connection_handle=0x{:x}", command_view.GetConnectionHandle());
@@ -3506,7 +3522,7 @@
 void DualModeController::LeLongTermKeyRequestReply(CommandView command) {
   auto command_view =
       bluetooth::hci::LeLongTermKeyRequestReplyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< LE Long Term Key Request Reply");
@@ -3523,7 +3539,7 @@
     CommandView command) {
   auto command_view =
       bluetooth::hci::LeLongTermKeyRequestNegativeReplyView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
   uint16_t handle = command_view.GetConnectionHandle();
 
   DEBUG(id_, "<< LE Long Term Key Request Negative Reply");
@@ -3540,7 +3556,7 @@
 void DualModeController::ReadConnectionAcceptTimeout(CommandView command) {
   auto command_view =
       bluetooth::hci::ReadConnectionAcceptTimeoutView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Connection Accept Timeout");
 
@@ -3553,7 +3569,7 @@
 void DualModeController::WriteConnectionAcceptTimeout(CommandView command) {
   auto command_view =
       bluetooth::hci::WriteConnectionAcceptTimeoutView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Connection Accept Timeout");
 
@@ -3567,7 +3583,7 @@
 
 void DualModeController::ReadLoopbackMode(CommandView command) {
   auto command_view = bluetooth::hci::ReadLoopbackModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Read Loopback Mode");
 
@@ -3577,7 +3593,7 @@
 
 void DualModeController::WriteLoopbackMode(CommandView command) {
   auto command_view = bluetooth::hci::WriteLoopbackModeView::Create(command);
-  ASSERT(command_view.IsValid());
+  CHECK_PACKET_VIEW(command_view);
 
   DEBUG(id_, "<< Write Loopback Mode");
   DEBUG(id_, "   loopback_mode={}",
diff --git a/tools/rootcanal/model/controller/dual_mode_controller.h b/tools/rootcanal/model/controller/dual_mode_controller.h
index b2e9b17..f3b0ce1 100644
--- a/tools/rootcanal/model/controller/dual_mode_controller.h
+++ b/tools/rootcanal/model/controller/dual_mode_controller.h
@@ -89,8 +89,8 @@
   /// to an external tracker. Packets are rejected if they failed to
   /// be parsed, or run into an unimplemented part of the controller.
   void RegisterInvalidPacketHandler(
-      std::function<void(uint32_t, InvalidPacketReason, std::string,
-                         std::vector<uint8_t> const&)>& handler);
+      const std::function<void(uint32_t, InvalidPacketReason, std::string,
+                               std::vector<uint8_t> const&)>& handler);
 
   // Set the callbacks for sending packets to the HCI.
   void RegisterEventChannel(
@@ -553,6 +553,24 @@
   void SendCommandCompleteUnknownOpCodeEvent(
       bluetooth::hci::OpCode op_code) const;
 
+  // Validate that a received packet is correctly formatted.
+  // If the packet failed to be parsed, the function sends a
+  // HCI Hardware Error event to the host and logs the packet to
+  // the configured handler.
+  template <typename T>
+  bool CheckPacketView(T const& view, std::string reason) {
+    if (view.IsValid()) {
+      return true;
+    }
+
+    // Send a hardware error to reset the host, and report the packet
+    // for tracing.
+    send_event_(bluetooth::hci::HardwareErrorBuilder::Create(0x43));
+    invalid_packet_handler_(id_, InvalidPacketReason::kParseError, reason,
+                            view.bytes().bytes());
+    return false;
+  }
+
   // Callbacks to send packets back to the HCI.
   std::function<void(std::shared_ptr<bluetooth::hci::AclBuilder>)> send_acl_;
   std::function<void(std::shared_ptr<bluetooth::hci::EventBuilder>)>
diff --git a/tools/rootcanal/model/controller/link_layer_controller.cc b/tools/rootcanal/model/controller/link_layer_controller.cc
index 71d950d..d0754bd 100644
--- a/tools/rootcanal/model/controller/link_layer_controller.cc
+++ b/tools/rootcanal/model/controller/link_layer_controller.cc
@@ -3165,6 +3165,7 @@
     // Save the original advertising type to report if the advertising
     // is connectable in the scan response report.
     scanner_.connectable_scan_response = connectable_advertising;
+    scanner_.extended_scan_response = false;
     scanner_.pending_scan_request = advertising_address;
     scanner_.pending_scan_request_timeout =
         std::chrono::steady_clock::now() + kScanRequestTimeout;
@@ -3588,6 +3589,7 @@
     // Save the original advertising type to report if the advertising
     // is connectable in the scan response report.
     scanner_.connectable_scan_response = connectable_advertising;
+    scanner_.extended_scan_response = true;
     scanner_.pending_scan_request = advertising_address;
 
     INFO(id_,
@@ -4963,15 +4965,36 @@
             resolved_advertising_address.GetAddressType());
     response.connectable_ = scanner_.connectable_scan_response;
     response.scannable_ = true;
-    response.legacy_ = true;
+    response.legacy_ = !scanner_.extended_scan_response;
     response.scan_response_ = true;
     response.primary_phy_ = bluetooth::hci::PrimaryPhyType::LE_1M;
+    // TODO: SID should be set in scan response PDU
     response.advertising_sid_ = 0xFF;
     response.tx_power_ = 0x7F;
-    response.advertising_data_ = scan_response.GetScanResponseData();
     response.rssi_ = rssi;
-    send_event_(
-        bluetooth::hci::LeExtendedAdvertisingReportBuilder::Create({response}));
+    response.direct_address_type_ =
+        bluetooth::hci::DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED;
+
+    // Each extended advertising report can only pass 229 bytes of
+    // advertising data (255 - size of report fields).
+    // RootCanal must fragment the report as necessary.
+    const size_t max_fragment_size = 229;
+    size_t offset = 0;
+    std::vector<uint8_t> advertising_data = scan_response.GetScanResponseData();
+
+    do {
+      size_t remaining_size = advertising_data.size() - offset;
+      size_t fragment_size = std::min(max_fragment_size, remaining_size);
+      response.data_status_ = remaining_size <= max_fragment_size
+                                  ? bluetooth::hci::DataStatus::COMPLETE
+                                  : bluetooth::hci::DataStatus::CONTINUING;
+      response.advertising_data_ =
+          std::vector(advertising_data.begin() + offset,
+                      advertising_data.begin() + offset + fragment_size);
+      offset += fragment_size;
+      send_event_(bluetooth::hci::LeExtendedAdvertisingReportBuilder::Create(
+          {response}));
+    } while (offset < advertising_data.size());
   }
 }
 
diff --git a/tools/rootcanal/model/controller/link_layer_controller.h b/tools/rootcanal/model/controller/link_layer_controller.h
index b7af7a3..e9645b8 100644
--- a/tools/rootcanal/model/controller/link_layer_controller.h
+++ b/tools/rootcanal/model/controller/link_layer_controller.h
@@ -1070,6 +1070,7 @@
 
     // Save information about the advertising PDU being scanned.
     bool connectable_scan_response;
+    bool extended_scan_response;
     std::optional<AddressWithType> pending_scan_request{};
     std::optional<std::chrono::steady_clock::time_point>
         pending_scan_request_timeout{};
diff --git a/tools/rootcanal/test/LL/DDI/SCN/BV_20_C.py b/tools/rootcanal/test/LL/DDI/SCN/BV_20_C.py
new file mode 100644
index 0000000..726e35a
--- /dev/null
+++ b/tools/rootcanal/test/LL/DDI/SCN/BV_20_C.py
@@ -0,0 +1,264 @@
+# 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 hci_packets as hci
+import link_layer_packets as ll
+import math
+import random
+from dataclasses import dataclass
+from hci_packets import ErrorCode
+from py.bluetooth import Address
+from py.controller import ControllerTest
+from typing import Optional
+
+ADV_IND = 0x13
+ADV_DIRECT_IND = 0x15
+ADV_SCAN_IND = 0x12
+ADV_EXT_IND = 0x02
+
+
+@dataclass
+class TestRound:
+    advertising_event_properties: int
+    target_address: Optional[Address]
+    scan_data_length: int
+
+
+class Test(ControllerTest):
+
+    # LL/DDI/SCN/BV-20-C  [Extended Scanning, Active – LE 1M PHY, Core 5.0]
+    #
+    # Verify that a scanner IUT detects and requests additional information from
+    # advertisements received and reports the results from the Controller. The
+    # Lower Tester advertises using scannable extended advertising events on one
+    # channel at a time and expects the IUT to report the advertising to the
+    # Upper Tester. Both directed and undirected advertising events are tested.
+    async def test(self):
+        # Test rounds.
+        # Note: some tests are skipped as no distinction is made between
+        # ADV_EXT_IND, AUX_ADV_IND, AUX_CHAIN_IND.
+        controller = self.controller
+        invalid_address = Address("11:22:33:44:55:66")
+        test_rounds = [
+            TestRound(ADV_IND, None, 0),
+            TestRound(ADV_IND, None, 31),
+            TestRound(ADV_SCAN_IND, None, 0),
+            TestRound(ADV_SCAN_IND, None, 31),
+            TestRound(ADV_EXT_IND, None, 1),
+            TestRound(ADV_EXT_IND, controller.address, 1),
+            TestRound(ADV_EXT_IND, invalid_address, 1),
+            TestRound(ADV_EXT_IND, None, 191),
+            TestRound(ADV_EXT_IND, None, 382),
+            TestRound(ADV_EXT_IND, None, 1647),
+        ]
+
+        # 1. For each round as specified in Table 4.2-35 based on Table 4.2-36, if ScanData Length is less
+        # than or equal to the “Scan Max Data” then perform steps 2–8 and otherwise omit this round.
+        for test_round in test_rounds:
+            await self.steps_2_8(**vars(test_round))
+
+    async def steps_2_8(self, advertising_event_properties: int, target_address: Optional[Address],
+                        scan_data_length: int):
+
+        controller = self.controller
+        lower_tester_address = Address("ca:fe:ca:fe:00:01")
+
+        # 2. The Upper Tester sends an HCI_LE_Set_Extended_Scan_Parameters command to the IUT. The
+        # Scanning_PHYs parameter is set as specified in Table 4.2-35, Scan_Type[0] set to 0x01 (Active
+        # Scanning), Scan_Interval[0] set to 0x0010, and Scan_Window[0] set to 0x0010.
+        # Own_Address_Type is set to 0x00 (Public Device Address), and Scanning_Filter_Policy is set to
+        # 0x00 (Accept All) and receives a successful HCI_Command_Complete.
+        controller.send_cmd(
+            hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.PUBLIC_DEVICE_ADDRESS,
+                                            scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL,
+                                            scanning_phys=0x1,
+                                            scanning_phy_parameters=[
+                                                hci.ScanningPhyParameters(le_scan_type=hci.LeScanType.ACTIVE,
+                                                                          le_scan_interval=0x0010,
+                                                                          le_scan_window=0x0010)
+                                            ]))
+
+        await self.expect_evt(
+            hci.LeSetExtendedScanParametersComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 3. The Upper Tester sends an HCI_LE_Set_Extended_Scan_Enable command to the IUT to enable
+        # scanning. Filter_Duplicates, Duration, and Period are all set to zero and receive a successful
+        # HCI_Command_Complete.
+        controller.send_cmd(
+            hci.LeSetExtendedScanEnable(enable=hci.Enable.ENABLED,
+                                        filter_duplicates=hci.Enable.DISABLED,
+                                        duration=0,
+                                        period=0))
+
+        await self.expect_evt(hci.LeSetExtendedScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
+
+        # 4. The Lower Tester begins advertising on the channel as specified in Table 4.2-35 using the PDU
+        # Type specified in Table 4.2-36 for this round. If AUX_ADV_IND is included in the round, the
+        # ADV_EXT_IND includes an AuxPtr that refers to the AUX_ADV_IND on the PHY as specified in
+        # Table 4.2-35, and all fields specified should be included with the AUX_ADV_IND only. If AdvA is
+        # specified, the appropriate PDU includes the field, where “LT” equals the Lower Tester address. If
+        # TargetA is specified, the appropriate PDU includes the field, where “IUT” equals the IUT address
+        # and “Not IUT” equals a random address other than the IUT address. Repeat for at least 20
+        # advertising intervals or until step 5 occurs.
+        connectable = (advertising_event_properties & 0x1) != 0
+        scannable = (advertising_event_properties & 0x2) != 0
+        directed = (advertising_event_properties & 0x4) != 0
+        high_duty_cycle = (advertising_event_properties & 0x8) != 0
+        legacy = (advertising_event_properties & 0x10) != 0
+
+        if legacy:
+            if advertising_event_properties == ADV_IND:
+                advertising_type = ll.LegacyAdvertisingType.ADV_IND
+            elif advertising_event_properties == ADV_DIRECT_IND:
+                advertising_type = ll.LegacyAdvertisingType.ADV_DIRECT_IND
+            elif advertising_event_properties == ADV_SCAN_IND:
+                advertising_type = ll.LegacyAdvertisingType.ADV_SCAN_IND
+            elif advertising_event_properties == ADV_NONCONN_IND:
+                advertising_type = ll.LegacyAdvertisingType.ADV_NONCONN_IND
+            pdu = ll.LeLegacyAdvertisingPdu(source_address=lower_tester_address,
+                                            destination_address=target_address or Address(),
+                                            advertising_address_type=ll.AddressType.PUBLIC,
+                                            target_address_type=ll.AddressType.PUBLIC,
+                                            advertising_type=advertising_type,
+                                            advertising_data=[])
+        else:
+            pdu = ll.LeExtendedAdvertisingPdu(source_address=lower_tester_address,
+                                              destination_address=target_address or Address(),
+                                              advertising_address_type=ll.AddressType.PUBLIC,
+                                              target_address_type=ll.AddressType.PUBLIC,
+                                              connectable=connectable,
+                                              scannable=scannable,
+                                              directed=not target_address is None,
+                                              sid=0,
+                                              tx_power=0x7f,
+                                              primary_phy=ll.PrimaryPhyType.LE_1M,
+                                              secondary_phy=ll.SecondaryPhyType.NO_PACKETS,
+                                              advertising_data=[])
+
+        # 5. For undirected advertisements or advertisements directed at the IUT, the Lower Tester receives
+        # either a SCAN_REQ (if advertising with legacy PDUs) or an AUX_SCAN_REQ (if advertising with
+        # extended PDUs) on the appropriate advertising channel. The ScanA field is set to the IUT’s
+        # address, and the AdvA address is set to the Lower Tester’s address. The Upper Tester receives
+        # an HCI_LE_Extended_Advertising_Report event from the IUT with an Event_Type where bit 3
+        # (Scan response) is not set, the Data Status in the Event_Type field is set to Complete (0b00),
+        # Periodic_Advertising_Interval is set to 0, and no advertising data. If the advertisements were
+        # directed but TargetA is not the IUT, skip to step 8.
+        for n in range(3):
+            if not legacy:
+                sid = random.randint(0, 15)
+                pdu.sid = sid
+            else:
+                sid = 0xff
+
+            controller.send_ll(pdu, rssi=0)
+
+            if target_address and target_address != controller.address:
+                # If the controller still emits an event, the error
+                # will appear in the subsequent rounds.
+                continue
+
+            await self.expect_evt(
+                hci.LeExtendedAdvertisingReport(responses=[
+                    hci.LeExtendedAdvertisingResponse(
+                        connectable=connectable,
+                        scannable=scannable,
+                        directed=not target_address is None,
+                        scan_response=False,
+                        legacy=legacy,
+                        data_status=hci.DataStatus.COMPLETE,
+                        address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
+                        address=lower_tester_address,
+                        primary_phy=hci.PrimaryPhyType.LE_1M,
+                        secondary_phy=hci.SecondaryPhyType.NO_PACKETS,
+                        advertising_sid=sid,
+                        tx_power=0x7f,
+                        rssi=0,
+                        periodic_advertising_interval=0,
+                        direct_address_type=hci.DirectAdvertisingAddressType.NO_ADDRESS_PROVIDED
+                        if not target_address else hci.DirectAdvertisingAddressType.PUBLIC_DEVICE_ADDRESS,
+                        direct_address=target_address or Address(),
+                        advertising_data=[])
+                ]))
+
+            await self.expect_ll(
+                ll.LeScan(source_address=controller.address,
+                          destination_address=lower_tester_address,
+                          scanning_address_type=ll.AddressType.PUBLIC,
+                          advertising_address_type=ll.AddressType.PUBLIC))
+
+            advertising_data = [random.randint(1, 254) for n in range(scan_data_length)]
+
+            # 6. Perform step 6A or 6B depending on the PDU sent by the IUT in step 5.
+            # Alternative 6A (The IUT sent a SCAN_REQ in step 5):
+            # 6A.1 The Lower Tester responds with a SCAN_RSP packet to the IUT T_IFS after the end
+            # of the SCAN_REQ PDU. If ScanData is specified, the SCAN_RSP PDU includes the
+            # field populated with random octets from 1 to 254 of the specified count.
+            # Alternative 6B (The IUT sent an AUX_SCAN_REQ in step 5):
+            # 6B.1 The Lower Tester responds with an AUX_SCAN_RSP packet to the IUT T_IFS after
+            # the end of the AUX_SCAN_REQ PDU with an AdvMode of 0b00. If ScanData is
+            # specified, the AUX_SCAN_RSP PDU includes the AdvData field populated with
+            # random octets from 1 to 254 of the specified count. If the ScanData is greater in
+            # length than will fit in one PDU, the Lower Tester includes an AuxPtr field and sends
+            # one or more AUX_CHAIN_IND PDUs containing the remaining data. Each PDU
+            # except the last contains as much AdvData as can fit.
+            controller.send_ll(ll.LeScanResponse(source_address=lower_tester_address,
+                                                 destination_address=controller.address,
+                                                 advertising_address_type=ll.AddressType.PUBLIC,
+                                                 scan_response_data=advertising_data),
+                               rssi=0)
+
+            # 7. If the Lower Tester sent a scan response in step 6, the Upper Tester receives one or more
+            # HCI_LE_Extended_Advertising_Report events from the IUT with an Event_Type where bit 3
+            # (Scan response) is set, and Periodic_Advertising_Interval is set to 0. If ScanData was included in
+            # the response, the Upper Tester receives the data included in one of the advertising packets. If
+            # ScanData is sent to the Upper Tester in multiple reports, the Data Status in the Event_Type field
+            # for each report except the last is set to “Incomplete, more data to come”, 0b01. The Event_Type
+            # field for the last report sent with advertisement data is set to “Complete”, 0b00.
+            offset = 0
+            max_fragment_length = 229
+            num_fragments = math.ceil(scan_data_length / max_fragment_length) or 1
+
+            for n in range(num_fragments):
+                remaining_length = scan_data_length - offset
+                fragment_length = min(max_fragment_length, remaining_length)
+                data_status = hci.DataStatus.CONTINUING if remaining_length > max_fragment_length else hci.DataStatus.COMPLETE
+                await self.expect_evt(
+                    hci.LeExtendedAdvertisingReport(responses=[
+                        hci.LeExtendedAdvertisingResponse(
+                            connectable=connectable,
+                            scannable=scannable,
+                            directed=False,
+                            scan_response=True,
+                            legacy=legacy,
+                            data_status=data_status,
+                            address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
+                            address=lower_tester_address,
+                            primary_phy=hci.PrimaryPhyType.LE_1M,
+                            secondary_phy=hci.SecondaryPhyType.NO_PACKETS,
+                            # TODO SID should be set in scan response PDU
+                            advertising_sid=0xff,
+                            tx_power=0x7f,
+                            rssi=0,
+                            periodic_advertising_interval=0,
+                            direct_address_type=hci.DirectAdvertisingAddressType.NO_ADDRESS_PROVIDED,
+                            direct_address=Address(),
+                            advertising_data=advertising_data[offset:offset + fragment_length])
+                    ]))
+                offset += fragment_length
+
+        # 8. The Upper Tester sends an HCI_LE_Set_Scan_Enable to the IUT to disable scanning and
+        # receives an HCI_Command_Complete event in response.
+        controller.send_cmd(hci.LeSetExtendedScanEnable(enable=hci.Enable.DISABLED))
+
+        await self.expect_evt(hci.LeSetExtendedScanEnableComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
diff --git a/tools/rootcanal/test/invalid_packet_handler_unittest.cc b/tools/rootcanal/test/invalid_packet_handler_unittest.cc
new file mode 100644
index 0000000..803124b
--- /dev/null
+++ b/tools/rootcanal/test/invalid_packet_handler_unittest.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "log.h"
+#include "model/controller/dual_mode_controller.h"
+
+namespace rootcanal {
+
+using namespace bluetooth::hci;
+
+class InvalidPacketHandlerTest : public ::testing::Test {
+ public:
+  InvalidPacketHandlerTest() = default;
+  ~InvalidPacketHandlerTest() override = default;
+
+ protected:
+  DualModeController controller_;
+};
+
+// Set Event Mask command with missing parameters.
+const std::vector<uint8_t> kInvalidCommandPacket = {0x01, 0x0C, 0x03,
+                                                    0xff, 0xff, 0xff};
+
+// Hardware Error event with code 0x43.
+const std::vector<uint8_t> kHardwareErrorEvent = {0x10, 0x01, 0x43};
+
+TEST_F(InvalidPacketHandlerTest, DefaultHandler) {
+  // Validate that the default invalid packet handler causes
+  // an abort when an invalid packet is received.
+  ASSERT_DEATH(controller_.HandleCommand(std::make_shared<std::vector<uint8_t>>(
+                   kInvalidCommandPacket)),
+               "");
+}
+
+TEST_F(InvalidPacketHandlerTest, RegisteredHandler) {
+  static struct {
+    uint32_t id;
+    InvalidPacketReason reason;
+    std::vector<uint8_t> bytes;
+  } invalid_packet;
+
+  static std::vector<uint8_t> hci_event;
+
+  // Validate that the registered invalid packet handler is correctly
+  // invoked when an invalid packet is received.
+  controller_.RegisterInvalidPacketHandler(
+      [&](uint32_t id, InvalidPacketReason reason, std::string,
+          std::vector<uint8_t> const& bytes) {
+        invalid_packet.id = id;
+        invalid_packet.reason = reason;
+        invalid_packet.bytes = bytes;
+      });
+
+  controller_.RegisterEventChannel(
+      [&](std::shared_ptr<std::vector<uint8_t>> packet) {
+        hci_event = std::vector<uint8_t>(*packet);
+      });
+
+  controller_.HandleCommand(
+      std::make_shared<std::vector<uint8_t>>(kInvalidCommandPacket));
+  ASSERT_EQ(invalid_packet.id, controller_.id_);
+  ASSERT_EQ(invalid_packet.reason, InvalidPacketReason::kParseError);
+  ASSERT_EQ(invalid_packet.bytes, kInvalidCommandPacket);
+  ASSERT_EQ(hci_event, kHardwareErrorEvent);
+}
+
+}  // namespace rootcanal
diff --git a/tools/rootcanal/test/main.py b/tools/rootcanal/test/main.py
index ecf8609..b61a5b8 100644
--- a/tools/rootcanal/test/main.py
+++ b/tools/rootcanal/test/main.py
@@ -58,6 +58,7 @@
     'LL.DDI.SCN.BV_14_C',
     'LL.DDI.SCN.BV_18_C',
     'LL.DDI.SCN.BV_19_C',
+    'LL.DDI.SCN.BV_20_C',
     'LL.DDI.SCN.BV_79_C',
     'LL.SEC.ADV.BV_11_C',
     'LMP.LIH.BV_01_C',