Merge "Remove incorrect logging for BTM_SecBond" into main
diff --git a/android/app/src/com/android/bluetooth/ObexServerSockets.java b/android/app/src/com/android/bluetooth/ObexServerSockets.java
index efd361d..b898cc7 100644
--- a/android/app/src/com/android/bluetooth/ObexServerSockets.java
+++ b/android/app/src/com/android/bluetooth/ObexServerSockets.java
@@ -18,10 +18,12 @@
 import android.annotation.SuppressLint;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothServerSocket;
 import android.bluetooth.BluetoothSocket;
 import android.util.Log;
 
+import com.android.bluetooth.btservice.AdapterService;
 import com.android.obex.ResponseCodes;
 import com.android.obex.ServerSession;
 
@@ -35,7 +37,7 @@
  * Use {@link #getRfcommChannel()} and {@link #getL2capPsm()} to get the channel numbers to put into
  * the SDP record.<br>
  * Call {@link #shutdown(boolean)} to terminate the accept threads created by the call to {@link
- * #create(IObexConnectionHandler)}.<br>
+ * #create(AdapterService, IObexConnectionHandler)}.<br>
  * A reference to an object of this type cannot be reused, and the {@link BluetoothServerSocket}
  * object references passed to this object will be closed by this object, hence cannot be reused
  * either (This is needed, as the only way to interrupt an accept call is to close the socket...)
@@ -49,6 +51,7 @@
 public class ObexServerSockets {
     private static final String TAG = ObexServerSockets.class.getSimpleName();
 
+    private final BluetoothAdapter mAdapter;
     private final IObexConnectionHandler mConHandler;
     /* The wrapped sockets */
     private final BluetoothServerSocket mRfcommSocket;
@@ -58,9 +61,11 @@
     private SocketAcceptThread mL2capThread;
 
     private ObexServerSockets(
+            BluetoothAdapter adapter,
             IObexConnectionHandler conHandler,
             BluetoothServerSocket rfcommSocket,
             BluetoothServerSocket l2capSocket) {
+        mAdapter = adapter;
         mConHandler = conHandler;
         mRfcommSocket = rfcommSocket;
         mL2capSocket = l2capSocket;
@@ -73,8 +78,10 @@
      *     an incoming connection.
      * @return a reference to a {@link ObexServerSockets} object instance.
      */
-    public static ObexServerSockets create(IObexConnectionHandler validator) {
+    public static ObexServerSockets create(
+            AdapterService adapterService, IObexConnectionHandler validator) {
         return create(
+                adapterService,
                 validator,
                 BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
                 BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
@@ -89,8 +96,10 @@
      *     an incoming connection.
      * @return a reference to a {@link ObexServerSockets} object instance.
      */
-    public static ObexServerSockets createInsecure(IObexConnectionHandler validator) {
+    public static ObexServerSockets createInsecure(
+            AdapterService adapterService, IObexConnectionHandler validator) {
         return create(
+                adapterService,
                 validator,
                 BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
                 BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
@@ -114,32 +123,35 @@
      */
     @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/350563786
     private static ObexServerSockets create(
-            IObexConnectionHandler validator, int rfcommChannel, int l2capPsm, boolean isSecure) {
+            AdapterService adapterService,
+            IObexConnectionHandler validator,
+            int rfcommChannel,
+            int l2capPsm,
+            boolean isSecure) {
         Log.d(TAG, "create(rfcomm = " + rfcommChannel + ", l2capPsm = " + l2capPsm + ")");
-        BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
-        if (bt == null) {
-            throw new RuntimeException("No bluetooth adapter...");
-        }
+
         BluetoothServerSocket rfcommSocket = null;
         BluetoothServerSocket l2capSocket = null;
         boolean initSocketOK = false;
 
+        final var adapter = adapterService.getSystemService(BluetoothManager.class).getAdapter();
+
         // It's possible that create will fail in some cases. retry for 10 times
         for (int i = 0; i < CREATE_RETRY_TIME; i++) {
             initSocketOK = true;
             try {
                 if (rfcommSocket == null) {
                     if (isSecure) {
-                        rfcommSocket = bt.listenUsingRfcommOn(rfcommChannel);
+                        rfcommSocket = adapter.listenUsingRfcommOn(rfcommChannel);
                     } else {
-                        rfcommSocket = bt.listenUsingInsecureRfcommOn(rfcommChannel);
+                        rfcommSocket = adapter.listenUsingInsecureRfcommOn(rfcommChannel);
                     }
                 }
                 if (l2capSocket == null) {
                     if (isSecure) {
-                        l2capSocket = bt.listenUsingL2capOn(l2capPsm);
+                        l2capSocket = adapter.listenUsingL2capOn(l2capPsm);
                     } else {
-                        l2capSocket = bt.listenUsingInsecureL2capOn(l2capPsm);
+                        l2capSocket = adapter.listenUsingInsecureL2capOn(l2capPsm);
                     }
                 }
             } catch (IOException e) {
@@ -152,7 +164,7 @@
             }
             if (!initSocketOK) {
                 // Need to break out of this loop if BT is being turned off.
-                int state = bt.getState();
+                int state = adapter.getState();
                 if ((state != BluetoothAdapter.STATE_TURNING_ON)
                         && (state != BluetoothAdapter.STATE_ON)) {
                     Log.w(TAG, "initServerSockets failed as BT is (being) turned off");
@@ -171,7 +183,8 @@
 
         if (initSocketOK) {
             Log.d(TAG, "Succeed to create listening sockets ");
-            ObexServerSockets sockets = new ObexServerSockets(validator, rfcommSocket, l2capSocket);
+            final ObexServerSockets sockets =
+                    new ObexServerSockets(adapter, validator, rfcommSocket, l2capSocket);
             sockets.startAccept();
             return sockets;
         } else {
@@ -230,8 +243,7 @@
     /** Signal to the {@link IObexConnectionHandler} that an error have occurred. */
     private synchronized void onAcceptFailed() {
         shutdown(false);
-        BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
-        if ((mAdapter != null) && (mAdapter.getState() == BluetoothAdapter.STATE_ON)) {
+        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) {
             Log.d(TAG, "onAcceptFailed() calling shutdown...");
             mConHandler.onAcceptFailed();
         }
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
index 92c6709..8ce3384 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -81,7 +81,6 @@
 
     private final A2dpNativeInterface mNativeInterface;
     private final A2dpCodecConfig mA2dpCodecConfig;
-    private final AdapterService mAdapterService;
     private final AudioManager mAudioManager;
     private final DatabaseManager mDatabaseManager;
     private final CompanionDeviceManager mCompanionDeviceManager;
@@ -117,7 +116,6 @@
     @VisibleForTesting
     A2dpService(AdapterService adapterService, A2dpNativeInterface nativeInterface, Looper looper) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mNativeInterface =
                 requireNonNullElseGet(
                         nativeInterface,
@@ -126,9 +124,8 @@
                                         adapterService,
                                         new A2dpNativeCallback(adapterService, this)));
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
-        mAudioManager = requireNonNull(mAdapterService.getSystemService(AudioManager.class));
-        mCompanionDeviceManager =
-                requireNonNull(mAdapterService.getSystemService(CompanionDeviceManager.class));
+        mAudioManager = requireNonNull(obtainSystemService(AudioManager.class));
+        mCompanionDeviceManager = requireNonNull(obtainSystemService(CompanionDeviceManager.class));
         mLooper = requireNonNull(looper);
         mHandler = new Handler(mLooper);
 
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index 037d85e..b02f5d2 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -57,7 +57,6 @@
     private final Object mActiveDeviceLock = new Object();
     private final Object mStreamHandlerLock = new Object();
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final A2dpSinkNativeInterface mNativeInterface;
     private final Looper mLooper;
@@ -80,7 +79,6 @@
     A2dpSinkService(
             AdapterService adapterService, A2dpSinkNativeInterface nativeInterface, Looper looper) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mNativeInterface = requireNonNull(nativeInterface);
         mLooper = looper;
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java
index 7f8823e..e739cc8 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java
@@ -24,6 +24,7 @@
 import com.android.bluetooth.IObexConnectionHandler;
 import com.android.bluetooth.ObexServerSockets;
 import com.android.bluetooth.audio_util.Image;
+import com.android.bluetooth.btservice.AdapterService;
 import com.android.obex.ServerSession;
 
 import java.io.IOException;
@@ -47,6 +48,7 @@
      */
     private static final int MAX_TRANSMIT_PACKET_SIZE = 1024;
 
+    private final AdapterService mAdapterService;
     // Cover Art and Image Handle objects
     private final AvrcpCoverArtStorage mStorage;
 
@@ -63,7 +65,9 @@
     private final AvrcpNativeInterface mNativeInterface;
 
     // The native interface must be a parameter here in order to be able to mock AvrcpTargetService
-    public AvrcpCoverArtService(AvrcpNativeInterface nativeInterface) {
+    public AvrcpCoverArtService(
+            AdapterService adapterService, AvrcpNativeInterface nativeInterface) {
+        mAdapterService = adapterService;
         mNativeInterface = nativeInterface;
         mAcceptThread = new SocketAcceptor();
         mStorage = new AvrcpCoverArtStorage(COVER_ART_STORAGE_MAX_ITEMS);
@@ -106,7 +110,7 @@
     private boolean startBipServer() {
         debug("Starting BIP OBEX server");
         synchronized (mServerLock) {
-            mServerSockets = ObexServerSockets.create(mAcceptThread);
+            mServerSockets = ObexServerSockets.create(mAdapterService, mAcceptThread);
             if (mServerSockets == null) {
                 error("Failed to get a server socket. Can't setup cover art service");
                 return false;
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
index 822a64c..a5eab30 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
@@ -19,6 +19,7 @@
 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
 
 import static java.util.Objects.requireNonNull;
+import static java.util.Objects.requireNonNullElseGet;
 
 import android.annotation.NonNull;
 import android.bluetooth.BluetoothDevice;
@@ -102,11 +103,10 @@
     public AvrcpTargetService(AdapterService adapterService) {
         this(
                 requireNonNull(adapterService),
-                adapterService.getSystemService(AudioManager.class),
+                null,
                 AvrcpNativeInterface.getInstance(adapterService),
                 new AvrcpVolumeManager(
                         requireNonNull(adapterService),
-                        adapterService.getSystemService(AudioManager.class),
                         AvrcpNativeInterface.getInstance(adapterService)),
                 Looper.myLooper());
     }
@@ -119,7 +119,8 @@
             AvrcpVolumeManager volumeManager,
             Looper looper) {
         super(requireNonNull(adapterService));
-        mAudioManager = requireNonNull(audioManager);
+        mAudioManager =
+                requireNonNullElseGet(audioManager, () -> obtainSystemService(AudioManager.class));
         mNativeInterface = requireNonNull(nativeInterface);
 
         mMediaPlayerList = new MediaPlayerList(adapterService, looper);
@@ -138,7 +139,7 @@
         mAvrcpVersion = AvrcpVersion.getCurrentSystemPropertiesValue();
         mVolumeManager = requireNonNull(volumeManager);
 
-        UserManager userManager = getApplicationContext().getSystemService(UserManager.class);
+        UserManager userManager = obtainSystemService(UserManager.class);
         if (userManager.isUserUnlocked()) {
             mMediaPlayerList.init(new ListCallback());
         }
@@ -147,7 +148,8 @@
             Log.e(TAG, "Please use AVRCP version 1.6 to enable cover art");
             mAvrcpCoverArtService = null;
         } else {
-            AvrcpCoverArtService coverArtService = new AvrcpCoverArtService(mNativeInterface);
+            AvrcpCoverArtService coverArtService =
+                    new AvrcpCoverArtService(adapterService, mNativeInterface);
             if (coverArtService.start()) {
                 mAvrcpCoverArtService = coverArtService;
             } else {
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
index 5daa0fd..3cd588e 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
@@ -173,12 +173,9 @@
      * <p>Fills {@code mVolumeMap} with content from {@link #getVolumeMap}, removing unbonded
      * devices if necessary.
      */
-    AvrcpVolumeManager(
-            AdapterService adapterService,
-            AudioManager audioManager,
-            AvrcpNativeInterface nativeInterface) {
+    AvrcpVolumeManager(AdapterService adapterService, AvrcpNativeInterface nativeInterface) {
         mAdapterService = adapterService;
-        mAudioManager = audioManager;
+        mAudioManager = mAdapterService.getSystemService(AudioManager.class);
         mNativeInterface = nativeInterface;
         mDeviceMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         mSafeMediaVolume = getSafeMediaVolume(adapterService, mDeviceMaxVolume);
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 5a77073..6e95905 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -97,7 +97,6 @@
 
     private final Object mActiveDeviceLock = new Object();
 
-    private final AdapterService mAdapterService;
     private final AvrcpControllerNativeInterface mNativeInterface;
     private final AvrcpCoverArtManager mCoverArtManager;
     private final boolean mCoverArtEnabled;
@@ -141,7 +140,6 @@
     public AvrcpControllerService(
             AdapterService adapterService, AvrcpControllerNativeInterface nativeInterface) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mNativeInterface = requireNonNull(nativeInterface);
         mNativeInterface.init(this);
 
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryService.java b/android/app/src/com/android/bluetooth/bas/BatteryService.java
index af31814..3162ace 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryService.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryService.java
@@ -55,7 +55,6 @@
 
     private static BatteryService sBatteryService;
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final HandlerThread mStateMachinesThread;
     private final Looper mStateMachinesLooper;
@@ -71,7 +70,6 @@
     @VisibleForTesting
     BatteryService(AdapterService adapterService, Looper looper) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mHandler = new Handler(requireNonNull(looper));
 
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 aabd2b0..103d604 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -174,7 +174,6 @@
             new ConcurrentHashMap<>();
     private final BassScanCallbackWrapper mBassScanCallback = new BassScanCallbackWrapper();
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final BluetoothAdapter mAdapter;
     private final PeriodicAdvertisingManager mPeriodicAdvertisingManager;
@@ -480,9 +479,8 @@
 
     public BassClientService(AdapterService adapterService) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
-        mAdapter = mAdapterService.getSystemService(BluetoothManager.class).getAdapter();
+        mAdapter = obtainSystemService(BluetoothManager.class).getAdapter();
         mPeriodicAdvertisingManager = mAdapter.getPeriodicAdvertisingManager();
         mHandler = new Handler(requireNonNull(Looper.getMainLooper()));
         mStateMachinesThread = new HandlerThread("BassClientService.StateMachines");
diff --git a/android/app/src/com/android/bluetooth/btservice/ProfileService.java b/android/app/src/com/android/bluetooth/btservice/ProfileService.java
index e8ea20d..d6acf61 100644
--- a/android/app/src/com/android/bluetooth/btservice/ProfileService.java
+++ b/android/app/src/com/android/bluetooth/btservice/ProfileService.java
@@ -18,7 +18,6 @@
 
 import android.annotation.SuppressLint;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.PackageManager;
 import android.os.IBinder;
@@ -33,6 +32,7 @@
         void cleanup();
     }
 
+    protected final AdapterService mAdapterService;
     private final IProfileServiceBinder mBinder;
     private final String mName;
     private boolean mAvailable = false;
@@ -71,8 +71,9 @@
         mTestModeEnabled = testModeEnabled;
     }
 
-    protected ProfileService(Context ctx) {
-        super(ctx);
+    protected ProfileService(AdapterService adapterService) {
+        super(adapterService);
+        mAdapterService = adapterService;
         mName = getName();
         Log.d(mName, "Service created");
         mBinder = initBinder();
@@ -132,6 +133,10 @@
                         PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS);
     }
 
+    protected <T> T obtainSystemService(Class<T> serviceClass) {
+        return mAdapterService.getSystemService(serviceClass);
+    }
+
     /**
      * Support dumping profile-specific information for dumpsys
      *
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index be2601b..61fc656 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -78,7 +78,6 @@
 
     private static CsipSetCoordinatorService sCsipSetCoordinatorService;
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final Handler mHandler;
     private final HandlerThread mStateMachinesThread;
@@ -116,7 +115,6 @@
             CsipSetCoordinatorNativeInterface nativeInterface,
             ServiceFactory serviceFactory) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mNativeInterface =
                 requireNonNullElseGet(
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index ea97c19..f092b96 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -158,7 +158,6 @@
      */
     private final HashMap<BluetoothDevice, Integer> mPermits = new HashMap<>();
 
-    private final AdapterService mAdapterService;
     private final ActivityManager mActivityManager;
     private final PackageManager mPackageManager;
     private final CompanionDeviceManager mCompanionDeviceManager;
@@ -170,11 +169,9 @@
 
     public GattService(AdapterService adapterService) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
-        mActivityManager = requireNonNull(mAdapterService.getSystemService(ActivityManager.class));
+        mActivityManager = requireNonNull(obtainSystemService(ActivityManager.class));
         mPackageManager = requireNonNull(mAdapterService.getPackageManager());
-        mCompanionDeviceManager =
-                requireNonNull(mAdapterService.getSystemService(CompanionDeviceManager.class));
+        mCompanionDeviceManager = requireNonNull(obtainSystemService(CompanionDeviceManager.class));
 
         Settings.Global.putInt(
                 getContentResolver(), "bluetooth_sanitized_exposure_notification_supported", 1);
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index ef9a778..662783b 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -79,7 +79,6 @@
     private final Map<BluetoothDevice, Integer> mDeviceCurrentPresetMap = new HashMap<>();
     private final Map<BluetoothDevice, Integer> mDeviceFeaturesMap = new HashMap<>();
     private final Map<BluetoothDevice, List<BluetoothHapPresetInfo>> mPresetsMap = new HashMap<>();
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final Handler mHandler;
     private final Looper mStateMachinesLooper;
@@ -130,7 +129,6 @@
             Looper looper,
             HapClientNativeInterface nativeInterface) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mNativeInterface =
                 requireNonNullElseGet(
                         nativeInterface,
diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
index b3126c9..f998ace 100644
--- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
+++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
@@ -68,7 +68,6 @@
 
     private static HearingAidService sHearingAidService;
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final HearingAidNativeInterface mNativeInterface;
     private final AudioManager mAudioManager;
@@ -99,7 +98,6 @@
             Looper looper,
             HearingAidNativeInterface nativeInterface) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         if (looper == null) {
             mHandler = new Handler(requireNonNull(Looper.getMainLooper()));
@@ -112,7 +110,7 @@
             mStateMachinesLooper = looper;
         }
         mNativeInterface = requireNonNull(nativeInterface);
-        mAudioManager = requireNonNull(mAdapterService.getSystemService(AudioManager.class));
+        mAudioManager = requireNonNull(obtainSystemService(AudioManager.class));
 
         setHearingAidService(this);
         mNativeInterface.init();
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
index afcb7ee..37416a0 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -125,7 +125,6 @@
     // Timeout for state machine thread join, to prevent potential ANR.
     private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1000;
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final HeadsetNativeInterface mNativeInterface;
     private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>();
@@ -176,7 +175,6 @@
     HeadsetService(
             AdapterService adapterService, HeadsetNativeInterface nativeInterface, Looper looper) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mNativeInterface = requireNonNull(nativeInterface);
         if (looper != null) {
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index 382c1f0..af88e4b 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -79,7 +79,6 @@
             new HashMap<>();
 
     private final HandlerThread mSmThread;
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final AudioManager mAudioManager;
     private final HeadsetClientNativeInterface mNativeInterface;
@@ -91,9 +90,8 @@
 
     public HeadsetClientService(AdapterService adapterService) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(adapterService.getDatabase());
-        mAudioManager = requireNonNull(mAdapterService.getSystemService(AudioManager.class));
+        mAudioManager = requireNonNull(obtainSystemService(AudioManager.class));
         mMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
         mMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
 
@@ -101,7 +99,7 @@
         mNativeInterface = HeadsetClientNativeInterface.getInstance(mAdapterService);
         mNativeInterface.initialize();
 
-        mBatteryManager = mAdapterService.getSystemService(BatteryManager.class);
+        mBatteryManager = obtainSystemService(BatteryManager.class);
 
         // start AudioManager in a known state
         mAudioManager.setHfpEnabled(false);
diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
index 8137b2b..5934155 100644
--- a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
@@ -75,7 +75,6 @@
     private static HidDeviceService sHidDeviceService;
 
     private final HidDeviceServiceHandler mHandler;
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final ActivityManager mActivityManager;
     private final HidDeviceNativeInterface mHidDeviceNativeInterface;
@@ -96,7 +95,6 @@
             Looper looper,
             HidDeviceNativeInterface nativeInterface) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
 
         mHandler = new HidDeviceServiceHandler(requireNonNull(looper));
@@ -104,7 +102,7 @@
                 requireNonNullElseGet(
                         nativeInterface, () -> new HidDeviceNativeInterface(adapterService));
         mHidDeviceNativeInterface.init();
-        mActivityManager = requireNonNull(mAdapterService.getSystemService(ActivityManager.class));
+        mActivityManager = requireNonNull(obtainSystemService(ActivityManager.class));
         mActivityManager.addOnUidImportanceListener(
                 mUidImportanceListener, FOREGROUND_IMPORTANCE_CUTOFF);
         setHidDeviceService(this);
diff --git a/android/app/src/com/android/bluetooth/hid/HidHostService.java b/android/app/src/com/android/bluetooth/hid/HidHostService.java
index 7cf235e..c4f80af 100644
--- a/android/app/src/com/android/bluetooth/hid/HidHostService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidHostService.java
@@ -100,7 +100,6 @@
     private final Map<BluetoothDevice, InputDevice> mInputDevices =
             Collections.synchronizedMap(new HashMap<>());
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final HidHostNativeInterface mNativeInterface;
 
@@ -126,11 +125,8 @@
 
     public HidHostService(AdapterService adapterService) {
         super(requireNonNull(adapterService));
-
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mNativeInterface = requireNonNull(HidHostNativeInterface.getInstance());
-
         mNativeInterface.init(this);
         setHidHostService(this);
     }
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 c432e51..4fd3e93 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -175,7 +175,6 @@
     private final ArrayDeque<BluetoothLeBroadcastSettings> mCreateBroadcastQueue =
             new ArrayDeque<>();
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final LeAudioNativeInterface mNativeInterface;
     private final HandlerThread mStateMachinesThread;
@@ -253,9 +252,8 @@
     LeAudioService(AdapterService adapterService, LeAudioNativeInterface nativeInterface) {
         super(requireNonNull(adapterService));
         mNativeInterface = requireNonNull(nativeInterface);
-        mAdapterService = requireNonNull(adapterService);
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
-        mAudioManager = requireNonNull(mAdapterService.getSystemService(AudioManager.class));
+        mAudioManager = requireNonNull(obtainSystemService(AudioManager.class));
 
         // Start handler thread for state machines
         mStateMachinesThread = new HandlerThread("LeAudioService.StateMachines");
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java b/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
index 3119036..9776172 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
@@ -32,6 +32,7 @@
 import com.android.bluetooth.BluetoothStatsLog;
 import com.android.bluetooth.IObexConnectionHandler;
 import com.android.bluetooth.ObexServerSockets;
+import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils;
 import com.android.bluetooth.map.BluetoothMapContentObserver.Msg;
 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
@@ -69,6 +70,7 @@
     static final int SDP_MAP_MAS_FEATURES_1_3 = 0x000603FF;
     static final int SDP_MAP_MAS_FEATURES_1_4 = 0x000603FF;
 
+    private final AdapterService mAdapterService;
     private final BluetoothAdapter mAdapter;
 
     private ServerSession mServerSession = null;
@@ -84,7 +86,6 @@
 
     private Handler mServiceHandler = null; // MAP service message handler
     private BluetoothMapService mMapService = null; // Handle to the outer MAP service
-    private Context mContext = null; // MAP service context
     private BluetoothMnsObexClient mMnsClient = null; // Shared MAP MNS client
     private BluetoothMapAccountItem mAccount = null; //
     private String mBaseUri = null; // Client base URI for this instance
@@ -114,15 +115,16 @@
 
     /** Create a e-mail MAS instance */
     BluetoothMapMasInstance(
+            AdapterService adapterService,
             BluetoothMapService mapService,
             BluetoothMapAccountItem account,
             int masId,
             boolean enableSmsMms) {
+        mAdapterService = adapterService;
         mObjectInstanceId = sInstanceCounter++;
         mMapService = mapService;
         mServiceHandler = mapService.getHandler();
-        mContext = mapService;
-        mAdapter = mContext.getSystemService(BluetoothManager.class).getAdapter();
+        mAdapter = mAdapterService.getSystemService(BluetoothManager.class).getAdapter();
         mAccount = account;
         if (account != null) {
             mBaseUri = account.mBase_uri;
@@ -276,8 +278,7 @@
         if (mServerSockets != null) {
             mAcceptNewConnections = true;
         } else {
-
-            mServerSockets = ObexServerSockets.create(this);
+            mServerSockets = ObexServerSockets.create(mAdapterService, this);
             mAcceptNewConnections = true;
 
             if (mServerSockets == null) {
@@ -387,11 +388,16 @@
             mMnsClient = mnsClient;
             mObserver =
                     new BluetoothMapContentObserver(
-                            mContext, mMnsClient, this, mAccount, mEnableSmsMms);
+                            mAdapterService, mMnsClient, this, mAccount, mEnableSmsMms);
             mObserver.init();
             mMapServer =
                     new BluetoothMapObexServer(
-                            mServiceHandler, mContext, mObserver, this, mAccount, mEnableSmsMms);
+                            mAdapterService,
+                            mServiceHandler,
+                            mObserver,
+                            this,
+                            mAccount,
+                            mEnableSmsMms);
             mMapServer.setRemoteFeatureMask(mRemoteFeatureMask);
             // setup transport
             BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket);
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
index 5257e07..59328da 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
@@ -103,10 +103,11 @@
 
     private static final int MAS_INSTANCE_INFORMATION_LENGTH = 200;
 
+    private final Context mContext;
+
     private BluetoothMapFolderElement mCurrentFolder;
     private BluetoothMapContentObserver mObserver = null;
     private Handler mCallback = null;
-    private final Context mContext;
     private boolean mIsAborted = false;
     BluetoothMapContent mOutContent;
     private String mBaseUriString = null;
@@ -126,16 +127,16 @@
     private ContentProviderClient mProviderClient = null;
 
     public BluetoothMapObexServer(
-            Handler callback,
             Context context,
+            Handler callback,
             BluetoothMapContentObserver observer,
             BluetoothMapMasInstance mas,
             BluetoothMapAccountItem account,
             boolean enableSmsMms)
             throws RemoteException {
         super();
-        mCallback = callback;
         mContext = context;
+        mCallback = callback;
         mObserver = observer;
         mEnableSmsMms = enableSmsMms;
         mAccount = account;
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapService.java b/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
index 46537c8..7128ec3 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
@@ -113,7 +113,6 @@
 
     private final MapBroadcastReceiver mMapReceiver = new MapBroadcastReceiver();
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final Handler mSessionStatusHandler;
     private final BluetoothMapAppObserver mAppObserver;
@@ -157,7 +156,6 @@
         super(requireNonNull(adapterService));
         BluetoothMap.invalidateBluetoothGetConnectionStateCache();
 
-        mAdapterService = requireNonNull(adapterService);
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
 
         setComponentAvailable(MAP_FILE_PROVIDER, true);
@@ -190,10 +188,8 @@
         registerReceiver(mMapReceiver, filterMessageSent);
         mAppObserver = new BluetoothMapAppObserver(this, this);
 
-        mSmsCapable =
-                requireNonNull(mAdapterService.getSystemService(TelephonyManager.class))
-                        .isSmsCapable();
-        mAlarmManager = requireNonNull(mAdapterService.getSystemService(AlarmManager.class));
+        mSmsCapable = requireNonNull(obtainSystemService(TelephonyManager.class)).isSmsCapable();
+        mAlarmManager = requireNonNull(obtainSystemService(AlarmManager.class));
 
         mEnabledAccounts = mAppObserver.getEnabledAccountItems();
         createMasInstances(); // Uses mEnabledAccounts
@@ -265,7 +261,7 @@
 
         // Acquire the wakeLock before starting Obex transaction thread
         if (mWakeLock == null) {
-            PowerManager pm = mAdapterService.getSystemService(PowerManager.class);
+            PowerManager pm = obtainSystemService(PowerManager.class);
             mWakeLock =
                     pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingObexMapTransaction");
             mWakeLock.setReferenceCounted(false);
@@ -444,7 +440,7 @@
                 case MSG_ACQUIRE_WAKE_LOCK:
                     Log.v(TAG, "Acquire Wake Lock request message");
                     if (mWakeLock == null) {
-                        PowerManager pm = mAdapterService.getSystemService(PowerManager.class);
+                        PowerManager pm = obtainSystemService(PowerManager.class);
                         mWakeLock =
                                 pm.newWakeLock(
                                         PowerManager.PARTIAL_WAKE_LOCK,
@@ -787,7 +783,7 @@
             Log.v(TAG, "  Adding account: " + account);
             int masId = getNextMasId();
             BluetoothMapMasInstance newInst =
-                    new BluetoothMapMasInstance(this, account, masId, false);
+                    new BluetoothMapMasInstance(mAdapterService, this, account, masId, false);
             mMasInstances.append(masId, newInst);
             mMasInstanceMap.put(account, newInst);
             // Start the new instance
@@ -849,7 +845,7 @@
         if (mSmsCapable) {
             // Add the SMS/MMS instance
             BluetoothMapMasInstance smsMmsInst =
-                    new BluetoothMapMasInstance(this, null, masId, true);
+                    new BluetoothMapMasInstance(mAdapterService, this, null, masId, true);
             mMasInstances.append(masId, smsMmsInst);
             mMasInstanceMap.put(null, smsMmsInst);
             masId++;
@@ -858,7 +854,7 @@
         // get list of accounts already set to be visible through MAP
         for (BluetoothMapAccountItem account : mEnabledAccounts) {
             BluetoothMapMasInstance newInst =
-                    new BluetoothMapMasInstance(this, account, masId, false);
+                    new BluetoothMapMasInstance(mAdapterService, this, account, masId, false);
             mMasInstances.append(masId, newInst);
             mMasInstanceMap.put(account, newInst);
             masId++;
@@ -974,7 +970,7 @@
                 PendingIntent.getBroadcast(this, 0, timeoutIntent, PendingIntent.FLAG_IMMUTABLE);
         pIntent.cancel();
 
-        AlarmManager alarmManager = this.getSystemService(AlarmManager.class);
+        AlarmManager alarmManager = obtainSystemService(AlarmManager.class);
         alarmManager.cancel(pIntent);
         mRemoveTimeoutMsg = false;
     }
diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
index 6a5c27b..92352dd 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
@@ -59,7 +59,6 @@
     private final Map<BluetoothDevice, MceStateMachine> mMapInstanceMap =
             new ConcurrentHashMap<>(1);
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final MnsService mMnsServer;
     private final Looper mStateMachinesLooper;
@@ -74,9 +73,8 @@
     @VisibleForTesting
     MapClientService(AdapterService adapterService, Looper looper, MnsService mnsServer) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(adapterService.getDatabase());
-        mMnsServer = requireNonNullElseGet(mnsServer, () -> new MnsService(this));
+        mMnsServer = requireNonNullElseGet(mnsServer, () -> new MnsService(mAdapterService, this));
 
         if (looper == null) {
             mHandler = new Handler(requireNonNull(Looper.getMainLooper()));
diff --git a/android/app/src/com/android/bluetooth/mapclient/MnsService.java b/android/app/src/com/android/bluetooth/mapclient/MnsService.java
index 1b5b945..501c48f 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MnsService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MnsService.java
@@ -26,6 +26,7 @@
 import com.android.bluetooth.IObexConnectionHandler;
 import com.android.bluetooth.ObexServerSockets;
 import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.obex.ServerSession;
 
@@ -39,6 +40,7 @@
     private static final int MNS_VERSION = 0x0104; // MAP version 1.4
 
     private final SocketAcceptor mAcceptThread = new SocketAcceptor();
+
     private final MapClientService mMapClientService;
 
     private ObexServerSockets mServerSockets;
@@ -46,10 +48,10 @@
     private volatile boolean mShutdown = false; // Used to interrupt socket accept thread
     private int mSdpHandle = -1;
 
-    MnsService(MapClientService service) {
+    MnsService(AdapterService adapterService, MapClientService service) {
         Log.v(TAG, "MnsService()");
         mMapClientService = service;
-        mServerSockets = ObexServerSockets.create(mAcceptThread);
+        mServerSockets = ObexServerSockets.create(adapterService, mAcceptThread);
         SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
         if (!nativeInterface.isAvailable()) {
             Log.e(TAG, "SdpManagerNativeInterface is not available");
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
index f9e1794..af98415 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
@@ -169,8 +169,6 @@
 
     boolean mAcceptNewConnections;
 
-    private final AdapterService mAdapterService;
-
     private static final String INVISIBLE =
             BluetoothShare.VISIBILITY + "=" + BluetoothShare.VISIBILITY_HIDDEN;
 
@@ -223,8 +221,6 @@
     BluetoothOppService(AdapterService adapterService, BluetoothOppPreference oppPreference) {
         super(requireNonNull(adapterService));
 
-        mAdapterService = adapterService;
-
         IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         registerReceiver(mBluetoothReceiver, filter);
@@ -504,7 +500,7 @@
     private void startSocketListener() {
         Log.d(TAG, "start Socket Listeners");
         stopListeners();
-        mServerSocket = ObexServerSockets.createInsecure(this);
+        mServerSocket = ObexServerSockets.createInsecure(mAdapterService, this);
         acceptNewConnections();
         SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
         if (!nativeInterface.isAvailable()) {
diff --git a/android/app/src/com/android/bluetooth/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java
index 5100110..4a1ba0f 100644
--- a/android/app/src/com/android/bluetooth/pan/PanService.java
+++ b/android/app/src/com/android/bluetooth/pan/PanService.java
@@ -76,7 +76,6 @@
             new ConcurrentHashMap<>();
 
     private final Map<String, IBluetoothPanCallback> mBluetoothTetheringCallbacks = new HashMap<>();
-    private final AdapterService mAdapterService;
     private final PanNativeInterface mNativeInterface;
     private final DatabaseManager mDatabaseManager;
     private final TetheringManager mTetheringManager;
@@ -113,13 +112,11 @@
     @VisibleForTesting
     PanService(AdapterService adapterService, PanNativeInterface nativeInterface, Looper looper) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mNativeInterface =
                 requireNonNullElseGet(nativeInterface, () -> new PanNativeInterface(this));
-        mUserManager = requireNonNull(mAdapterService.getSystemService(UserManager.class));
-        mTetheringManager =
-                requireNonNull(mAdapterService.getSystemService(TetheringManager.class));
+        mUserManager = requireNonNull(obtainSystemService(UserManager.class));
+        mTetheringManager = requireNonNull(obtainSystemService(TetheringManager.class));
         mHandler = new PanServiceHandler(looper);
 
         int maxPanDevice;
@@ -302,7 +299,7 @@
             IBluetoothPanCallback callback, int id, int callerUid, boolean value) {
         Log.d(TAG, "setBluetoothTethering: " + value + ", mTetherOn: " + mTetherOn);
 
-        UserManager um = mAdapterService.getSystemService(UserManager.class);
+        UserManager um = obtainSystemService(UserManager.class);
         if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING) && value) {
             throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
         }
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
index a8a4c72..bdcfcc7 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
@@ -26,6 +26,7 @@
 import static com.android.bluetooth.Utils.joinUninterruptibly;
 
 import static java.util.Objects.requireNonNull;
+import static java.util.Objects.requireNonNullElseGet;
 
 import android.app.Activity;
 import android.app.Notification;
@@ -150,7 +151,6 @@
     private final BluetoothPbapContentObserver mContactChangeObserver =
             new BluetoothPbapContentObserver();
 
-    private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final NotificationManager mNotificationManager;
     private final PbapHandler mSessionStatusHandler;
@@ -173,17 +173,16 @@
     private static BluetoothPbapService sBluetoothPbapService;
 
     public BluetoothPbapService(AdapterService adapterService) {
-        this(
-                requireNonNull(adapterService),
-                adapterService.getSystemService(NotificationManager.class));
+        this(requireNonNull(adapterService), null);
     }
 
     @VisibleForTesting
     BluetoothPbapService(AdapterService adapterService, NotificationManager notificationManager) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
-        mNotificationManager = requireNonNull(notificationManager);
+        mNotificationManager =
+                requireNonNullElseGet(
+                        notificationManager, () -> obtainSystemService(NotificationManager.class));
 
         IntentFilter userFilter = new IntentFilter();
         userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -360,7 +359,7 @@
                         return;
                     }
                     Log.d(TAG, "Got " + action + " to userId " + userId);
-                    UserManager userManager = mAdapterService.getSystemService(UserManager.class);
+                    UserManager userManager = obtainSystemService(UserManager.class);
                     if (userManager.isUserUnlocked(UserHandle.of(userId))) {
                         sendUpdateRequest();
                     }
@@ -517,7 +516,8 @@
 
             switch (msg.what) {
                 case START_LISTENER:
-                    mServerSockets = ObexServerSockets.create(BluetoothPbapService.this);
+                    mServerSockets =
+                            ObexServerSockets.create(mAdapterService, BluetoothPbapService.this);
                     if (mServerSockets == null) {
                         Log.w(TAG, "ObexServerSockets.create() returned null");
                         ContentProfileErrorReportUtils.report(
@@ -549,7 +549,7 @@
                     break;
                 case MSG_ACQUIRE_WAKE_LOCK:
                     if (mWakeLock == null) {
-                        PowerManager pm = mAdapterService.getSystemService(PowerManager.class);
+                        PowerManager pm = obtainSystemService(PowerManager.class);
                         mWakeLock =
                                 pm.newWakeLock(
                                         PowerManager.PARTIAL_WAKE_LOCK,
@@ -913,7 +913,7 @@
     }
 
     private void getLocalTelephonyDetails() {
-        TelephonyManager tm = mAdapterService.getSystemService(TelephonyManager.class);
+        TelephonyManager tm = obtainSystemService(TelephonyManager.class);
         if (tm != null) {
             sLocalPhoneNum = tm.getLine1Number();
             sLocalPhoneName = this.getString(R.string.localPhoneName);
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
index 1e0f5b0..4405965 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
@@ -70,7 +70,6 @@
     final Map<BluetoothDevice, PbapClientStateMachineOld> mPbapClientStateMachineOldMap =
             new ConcurrentHashMap<>();
 
-    private final AdapterService mAdapterService;
     private final PbapClientContactsStorage mPbapClientContactsStorage;
     private final PbapClientAccountManager mPbapClientAccountManager;
     private final DatabaseManager mDatabaseManager;
@@ -117,7 +116,6 @@
 
     public PbapClientService(AdapterService adapterService) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mHandler = new Handler(Looper.getMainLooper());
 
@@ -147,7 +145,6 @@
             PbapClientContactsStorage storage,
             Map<BluetoothDevice, PbapClientStateMachine> deviceMap) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mHandler = new Handler(Looper.getMainLooper());
 
@@ -725,7 +722,6 @@
     @VisibleForTesting
     PbapClientService(AdapterService adapterService, PbapClientAccountManager accountManager) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mHandler = new Handler(Looper.getMainLooper());
 
diff --git a/android/app/src/com/android/bluetooth/sap/SapService.java b/android/app/src/com/android/bluetooth/sap/SapService.java
index 8815fad..0d28825 100644
--- a/android/app/src/com/android/bluetooth/sap/SapService.java
+++ b/android/app/src/com/android/bluetooth/sap/SapService.java
@@ -97,7 +97,6 @@
             "com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT";
     private static final int USER_CONFIRM_TIMEOUT_VALUE = 25000;
 
-    private final AdapterService mAdapterService;
     private final BluetoothAdapter mAdapter;
 
     private PowerManager.WakeLock mWakeLock = null;
@@ -122,8 +121,7 @@
 
     public SapService(AdapterService adapterService) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
-        mAdapter = mAdapterService.getSystemService(BluetoothManager.class).getAdapter();
+        mAdapter = obtainSystemService(BluetoothManager.class).getAdapter();
         BluetoothSap.invalidateBluetoothGetConnectionStateCache();
 
         IntentFilter filter = new IntentFilter();
@@ -295,7 +293,7 @@
 
         // acquire the wakeLock before start SAP transaction thread
         if (mWakeLock == null) {
-            PowerManager pm = mAdapterService.getSystemService(PowerManager.class);
+            PowerManager pm = obtainSystemService(PowerManager.class);
             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingSapTransaction");
             mWakeLock.setReferenceCounted(false);
             mWakeLock.acquire();
@@ -488,8 +486,7 @@
                         case MSG_ACQUIRE_WAKE_LOCK:
                             Log.v(TAG, "Acquire Wake Lock request message");
                             if (mWakeLock == null) {
-                                PowerManager pm =
-                                        mAdapterService.getSystemService(PowerManager.class);
+                                PowerManager pm = obtainSystemService(PowerManager.class);
                                 mWakeLock =
                                         pm.newWakeLock(
                                                 PowerManager.PARTIAL_WAKE_LOCK,
@@ -737,7 +734,7 @@
     private void cancelUserTimeoutAlarm() {
         Log.d(TAG, "cancelUserTimeOutAlarm()");
         if (mAlarmManager == null) {
-            mAlarmManager = this.getSystemService(AlarmManager.class);
+            mAlarmManager = obtainSystemService(AlarmManager.class);
         }
         if (mRemoveTimeoutMsg) {
             Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
index a355c49..1036760 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
@@ -89,7 +89,6 @@
     final RemoteCallbackList<IBluetoothVolumeControlCallback> mCallbacks =
             new RemoteCallbackList<>();
 
-    private final AdapterService mAdapterService;
     private final AudioManager mAudioManager;
     private final DatabaseManager mDatabaseManager;
     private final Handler mHandler;
@@ -122,7 +121,6 @@
             Looper looper,
             VolumeControlNativeInterface nativeInterface) {
         super(requireNonNull(adapterService));
-        mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mNativeInterface =
                 requireNonNullElseGet(
@@ -130,7 +128,7 @@
                         () ->
                                 new VolumeControlNativeInterface(
                                         new VolumeControlNativeCallback(adapterService, this)));
-        mAudioManager = requireNonNull(mAdapterService.getSystemService(AudioManager.class));
+        mAudioManager = requireNonNull(obtainSystemService(AudioManager.class));
         if (looper == null) {
             mHandler = new Handler(requireNonNull(Looper.getMainLooper()));
             mStateMachinesThread = new HandlerThread("VolumeControlService.StateMachines");
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java
index 41ea02a..4fb2aa6 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java
@@ -91,7 +91,6 @@
         mLooper.startAutoDispatch();
 
         mockGetSystemService(mMockAdapterService, AudioManager.class, mMockAudioManager);
-
         mockGetSystemService(mMockAdapterService, MediaSessionManager.class, mMediaSessionManager);
 
         doReturn(mLooper.getNewExecutor()).when(mMockAdapterService).getMainExecutor();
@@ -153,8 +152,7 @@
     @Test
     public void testServiceInstance() {
         AvrcpVolumeManager volumeManager =
-                new AvrcpVolumeManager(
-                        mMockAdapterService, mMockAudioManager, mMockNativeInterface);
+                new AvrcpVolumeManager(mMockAdapterService, mMockNativeInterface);
         AvrcpTargetService service =
                 new AvrcpTargetService(
                         mMockAdapterService,
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpVolumeManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpVolumeManagerTest.java
index 06cafa6..2574ea7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpVolumeManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpVolumeManagerTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.bluetooth.TestUtils.MockitoRule;
 import static com.android.bluetooth.TestUtils.getTestDevice;
+import static com.android.bluetooth.TestUtils.mockGetSystemService;
 import static com.android.bluetooth.avrcp.AvrcpVolumeManager.AVRCP_MAX_VOL;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -58,7 +59,7 @@
     @Mock private Resources mResources;
     @Mock private AvrcpNativeInterface mNativeInterface;
     @Mock private AdapterService mAdapterService;
-    @Mock AudioManager mAudioManager;
+    @Mock private AudioManager mAudioManager;
 
     private static final int TEST_DEVICE_MAX_VOLUME = 25;
 
@@ -79,8 +80,8 @@
                                 testName.getMethodName() + "TmpPref", Context.MODE_PRIVATE))
                 .when(mAdapterService)
                 .getSharedPreferences(anyString(), anyInt());
-        mAvrcpVolumeManager =
-                new AvrcpVolumeManager(mAdapterService, mAudioManager, mNativeInterface);
+        mockGetSystemService(mAdapterService, AudioManager.class, mAudioManager);
+        mAvrcpVolumeManager = new AvrcpVolumeManager(mAdapterService, mNativeInterface);
     }
 
     @Test
@@ -105,14 +106,12 @@
     @Test
     public void sendVolumeChanged() {
         mAvrcpVolumeManager.sendVolumeChanged(mDevice, TEST_DEVICE_MAX_VOLUME);
-
         verify(mNativeInterface).sendVolumeChanged(mDevice, AVRCP_MAX_VOL);
     }
 
     @Test
     public void setVolume() {
         mAvrcpVolumeManager.setVolume(mDevice, AVRCP_MAX_VOL);
-
         verify(mAudioManager)
                 .setStreamVolume(
                         eq(AudioManager.STREAM_MUSIC), eq(TEST_DEVICE_MAX_VOLUME), anyInt());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMasInstanceTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMasInstanceTest.java
index 3435eb7..98c7d6e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMasInstanceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMasInstanceTest.java
@@ -17,21 +17,19 @@
 package com.android.bluetooth.map;
 
 import static com.android.bluetooth.TestUtils.MockitoRule;
+import static com.android.bluetooth.TestUtils.mockContextGetBluetoothManager;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
-import android.bluetooth.BluetoothManager;
-import android.content.Context;
 import android.graphics.drawable.ColorDrawable;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.bluetooth.TestUtils.MockitoRule;
+import com.android.bluetooth.btservice.AdapterService;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -45,6 +43,7 @@
 public class BluetoothMapMasInstanceTest {
     @Rule public final MockitoRule mMockitoRule = new MockitoRule();
 
+    @Mock private AdapterService mAdapterService;
     @Mock private BluetoothMapService mMapService;
 
     private static final int TEST_MAS_ID = 1;
@@ -76,14 +75,15 @@
 
     @Test
     public void toString_returnsInfo() {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final BluetoothManager manager = context.getSystemService(BluetoothManager.class);
-        assertThat(manager).isNotNull();
-        doReturn(manager).when(mMapService).getSystemService(BluetoothManager.class);
+        mockContextGetBluetoothManager(mAdapterService);
 
         BluetoothMapMasInstance instance =
                 new BluetoothMapMasInstance(
-                        mMapService, mAccountItem, TEST_MAS_ID, TEST_ENABLE_SMS_MMS);
+                        mAdapterService,
+                        mMapService,
+                        mAccountItem,
+                        TEST_MAS_ID,
+                        TEST_ENABLE_SMS_MMS);
 
         String expected =
                 "MasId: "
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java
index 1edd67c..f4d6145 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java
@@ -17,6 +17,7 @@
 package com.android.bluetooth.map;
 
 import static com.android.bluetooth.TestUtils.MockitoRule;
+import static com.android.bluetooth.TestUtils.mockContextGetBluetoothManager;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -28,7 +29,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import android.bluetooth.BluetoothManager;
 import android.content.ContentProviderClient;
 import android.content.Context;
 import android.database.MatrixCursor;
@@ -37,11 +37,11 @@
 import android.os.RemoteException;
 
 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.MockitoRule;
+import com.android.bluetooth.btservice.AdapterService;
 import com.android.obex.ResponseCodes;
 
 import org.junit.Before;
@@ -58,6 +58,7 @@
     @Rule public final MockitoRule mMockitoRule = new MockitoRule();
 
     @Mock private Context mMockContext;
+    @Mock private AdapterService mAdapterService;
     @Mock private BluetoothMapService mMapService;
     @Mock private ContentProviderClient mProviderClient;
     @Mock private BluetoothMapContentObserver mObserver;
@@ -98,19 +99,20 @@
                         TEST_UCI,
                         TEST_UCI_PREFIX);
 
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final BluetoothManager manager = context.getSystemService(BluetoothManager.class);
-        assertThat(manager).isNotNull();
-        doReturn(manager).when(mMapService).getSystemService(BluetoothManager.class);
+        mockContextGetBluetoothManager(mAdapterService);
 
         mMasInstance =
                 new BluetoothMapMasInstance(
-                        mMapService, mAccountItem, TEST_MAS_ID, TEST_ENABLE_SMS_MMS);
+                        mAdapterService,
+                        mMapService,
+                        mAccountItem,
+                        TEST_MAS_ID,
+                        TEST_ENABLE_SMS_MMS);
         mParams = new BluetoothMapAppParams();
         mObexServer =
                 new BluetoothMapObexServer(
-                        null,
                         mMockContext,
+                        null,
                         mObserver,
                         mMasInstance,
                         mAccountItem,
@@ -132,8 +134,8 @@
                         TEST_UCI_PREFIX);
         BluetoothMapObexServer obexServer =
                 new BluetoothMapObexServer(
-                        null,
                         mMockContext,
+                        null,
                         mObserver,
                         mMasInstance,
                         accountItemWithTypeEmail,
@@ -235,7 +237,7 @@
     public void setMsgTypeFilterParams_withAccountNull_andOverwriteTrue() throws Exception {
         BluetoothMapObexServer obexServer =
                 new BluetoothMapObexServer(
-                        null, mMockContext, mObserver, mMasInstance, null, false);
+                        mMockContext, null, mObserver, mMasInstance, null, false);
 
         obexServer.setMsgTypeFilterParams(mParams, true);
 
@@ -262,8 +264,8 @@
                         TEST_UCI_PREFIX);
         BluetoothMapObexServer obexServer =
                 new BluetoothMapObexServer(
-                        null,
                         mMockContext,
+                        null,
                         mObserver,
                         mMasInstance,
                         accountItemWithTypeEmail,
@@ -289,8 +291,8 @@
                         TEST_UCI_PREFIX);
         BluetoothMapObexServer obexServer =
                 new BluetoothMapObexServer(
-                        null,
                         mMockContext,
+                        null,
                         mObserver,
                         mMasInstance,
                         accountItemWithTypeIm,
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java
index 749df0e..a2b0688 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java
@@ -20,6 +20,7 @@
 
 import static com.android.bluetooth.TestUtils.MockitoRule;
 import static com.android.bluetooth.TestUtils.getTestDevice;
+import static com.android.bluetooth.TestUtils.mockContextGetBluetoothManager;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -109,6 +110,7 @@
         doNothing().when(mMethodProxy).threadStart(any());
         mTestLooper.startAutoDispatch();
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
+        mockContextGetBluetoothManager(mAdapterService);
         mService = new BluetoothPbapService(mAdapterService, mNotificationManager);
         mService.setAvailable(true);
 
diff --git a/flags/le_scanning.aconfig b/flags/le_scanning.aconfig
index 0b4985e..c86ac7d 100644
--- a/flags/le_scanning.aconfig
+++ b/flags/le_scanning.aconfig
@@ -10,3 +10,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "support_passive_scanning"
+    namespace: "bluetooth"
+    description: "Support passive scannign by delivering adv packet w/o scan response"
+    bug: "414501027"
+}
diff --git a/flags/leaudio.aconfig b/flags/leaudio.aconfig
index 1609a2b..1900845 100644
--- a/flags/leaudio.aconfig
+++ b/flags/leaudio.aconfig
@@ -358,3 +358,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "dsa_configure_first_cis"
+    namespace: "bluetooth"
+    description: "Configure only the first CIS for DSA"
+    bug: "417561299"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/rfcomm.aconfig b/flags/rfcomm.aconfig
index a46f9f6..1374c76 100644
--- a/flags/rfcomm.aconfig
+++ b/flags/rfcomm.aconfig
@@ -40,3 +40,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "rfcomm_fix_bta_ag_rfc_acp_open_error"
+    namespace: "bluetooth"
+    description: "Properly handle bta_ag_rfc_acp_open when check connection fail"
+    bug: "417997444"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index 33c0d10..28a40c5 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -206,15 +206,18 @@
  *
  ******************************************************************************/
 static void bta_dm_deinit_cb(void) {
-  /*
-   * TODO: Should alarm_free() the bta_dm_cb timers during graceful
-   * shutdown.
-   */
   alarm_free(bta_dm_cb.disable_timer);
   alarm_free(bta_dm_cb.switch_delay_timer);
+  if (com::android::bluetooth::flags::set_ptr_null_after_free()) {
+    bta_dm_cb.switch_delay_timer = nullptr;
+    bta_dm_cb.disable_timer = nullptr;
+  }
   for (size_t i = 0; i < BTA_DM_NUM_PM_TIMER; i++) {
     for (size_t j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) {
       alarm_free(bta_dm_cb.pm_timer[i].timer[j]);
+      if (com::android::bluetooth::flags::set_ptr_null_after_free()) {
+        bta_dm_cb.pm_timer[i].timer[j] = nullptr;
+      }
     }
   }
   bta_dm_cb.pending_removals.clear();
diff --git a/system/bta/dm/bta_dm_device_search.cc b/system/bta/dm/bta_dm_device_search.cc
index b1e905f..67bcd2c 100644
--- a/system/bta/dm/bta_dm_device_search.cc
+++ b/system/bta/dm/bta_dm_device_search.cc
@@ -179,7 +179,7 @@
 
   result.inq_res.bd_addr = p_inq->remote_bd_addr;
 
-  // Pass the original address to GattService#onScanResult
+  // Pass the original address to ScanController#onScanResult
   result.inq_res.original_bda = p_inq->original_bda;
 
   result.inq_res.dev_class = p_inq->dev_class;
@@ -208,6 +208,8 @@
   }
 
   if (bta_dm_search_cb.p_device_search_cback) {
+    log::debug("Inquiry results callback from BTM, bda={}, original_bda={}", result.inq_res.bd_addr,
+               result.inq_res.original_bda);
     bta_dm_search_cb.p_device_search_cback(BTA_DM_INQ_RES_EVT, &result);
   }
 
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index 95cba20..6dec98a 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -57,8 +57,8 @@
         },
         host: {
             srcs: [
-                "hal/hci_hal_host_rootcanal.cc",
                 "hal/socket_hal_host.cc",
+                "hal/hci_hal_impl_host_rootcanal.cc",
                 "os/host/parameter_provider.cc",
                 "os/host/system_properties.cc",
                 "os/host/wakelock_native.cc",
@@ -69,7 +69,7 @@
             srcs: [
                 "hal/hci_backend_aidl.cc",
                 "hal/hci_backend_hidl.cc",
-                "hal/hci_hal_android.cc",
+                "hal/hci_hal_impl_android.cc",
                 "hal/ranging_hal_impl_android.cc",
                 "hal/socket_hal_android.cc",
                 "os/android/parameter_provider.cc",
@@ -320,13 +320,13 @@
         },
         host: {
             srcs: [
-                "hal/hci_hal_host_test.cc",
+                "hal/hci_hal_impl_host_rootcanal_test.cc",
                 "sysprops/sysprops_module_test.cc",
             ],
         },
         android: {
             srcs: [
-                "hal/hci_hal_android_test.cc",
+                "hal/hci_hal_impl_android_test.cc",
                 "os/android/wakelock_native_test.cc",
             ],
             static_libs: [
diff --git a/system/gd/BUILD.gn b/system/gd/BUILD.gn
index 90f17c3..aed96ed 100644
--- a/system/gd/BUILD.gn
+++ b/system/gd/BUILD.gn
@@ -99,9 +99,9 @@
   ]
 
   if (use.floss_rootcanal) {
-    sources += [ "hal/hci_hal_host_rootcanal.cc" ]
+    sources += [ "hal/hci_hal_impl_host_rootcanal.cc" ]
   } else {
-    sources += [ "hal/hci_hal_host.cc" ]
+    sources += [ "hal/hci_hal_impl_host.cc" ]
   }
 
   include_dirs = [ "." ]
diff --git a/system/gd/hal/fuzz/fuzz_hci_hal.cc b/system/gd/hal/fuzz/fuzz_hci_hal.cc
index 3d9afec..1e7b548 100644
--- a/system/gd/hal/fuzz/fuzz_hci_hal.cc
+++ b/system/gd/hal/fuzz/fuzz_hci_hal.cc
@@ -111,8 +111,6 @@
   callbacks_->isoDataReceived(data);
 }
 
-const ModuleFactory FuzzHciHal::Factory = ModuleFactory([]() { return new FuzzHciHal(); });
-
 }  // namespace fuzz
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/system/gd/hal/fuzz/fuzz_hci_hal.h b/system/gd/hal/fuzz/fuzz_hci_hal.h
index ab9fc7e..e144932 100644
--- a/system/gd/hal/fuzz/fuzz_hci_hal.h
+++ b/system/gd/hal/fuzz/fuzz_hci_hal.h
@@ -36,15 +36,6 @@
 
   void injectArbitrary(FuzzedDataProvider& fdp);
 
-  std::string ToString() const override { return "HciHalFuzz"; }
-
-  static const ModuleFactory Factory;
-
-protected:
-  void ListDependencies(ModuleList* /* list */) const override {}
-  void Start() override {}
-  void Stop() override {}
-
 private:
   void injectAclData(std::vector<uint8_t> data);
   void injectHciEvent(std::vector<uint8_t> data);
diff --git a/system/gd/hal/hci_hal.h b/system/gd/hal/hci_hal.h
index 2c028c3..dba985a 100644
--- a/system/gd/hal/hci_hal.h
+++ b/system/gd/hal/hci_hal.h
@@ -18,8 +18,6 @@
 
 #include <vector>
 
-#include "module.h"
-
 namespace bluetooth {
 namespace hal {
 
@@ -65,10 +63,8 @@
 // the stack and abstracts away power management, initialization, and other
 // implementation-specific details related to the hardware.
 // LINT.IfChange
-class HciHal : public ::bluetooth::Module {
+class HciHal {
 public:
-  static const ModuleFactory Factory;
-
   virtual ~HciHal() = default;
 
   // Register the callback for incoming packets. All incoming packets are dropped before
diff --git a/system/gd/hal/hci_hal_fake.cc b/system/gd/hal/hci_hal_fake.cc
index 781dde6..2a5ee1a 100644
--- a/system/gd/hal/hci_hal_fake.cc
+++ b/system/gd/hal/hci_hal_fake.cc
@@ -84,6 +84,5 @@
   callbacks->hciEventReceived(view);
 }
 
-const ModuleFactory TestHciHal::Factory = ModuleFactory([]() { return new TestHciHal(); });
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/system/gd/hal/hci_hal_fake.h b/system/gd/hal/hci_hal_fake.h
index 9d58064..1436fde 100644
--- a/system/gd/hal/hci_hal_fake.h
+++ b/system/gd/hal/hci_hal_fake.h
@@ -72,16 +72,6 @@
 
   void InjectEvent(std::unique_ptr<packet::BasePacketBuilder> event);
 
-  void Start() {}
-
-  void Stop() {}
-
-  void ListDependencies(ModuleList* /* list */) const {}
-
-  std::string ToString() const override { return std::string("TestHciHal"); }
-
-  static const ModuleFactory Factory;
-
 private:
   common::BlockingQueue<hal::HciPacket> outgoing_commands_;
   common::BlockingQueue<hal::HciPacket> outgoing_acl_;
diff --git a/system/gd/hal/hci_hal_host_rootcanal.cc b/system/gd/hal/hci_hal_host_rootcanal.cc
deleted file mode 100644
index a15a69a..0000000
--- a/system/gd/hal/hci_hal_host_rootcanal.cc
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <bluetooth/log.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <poll.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <csignal>
-#include <mutex>
-#include <queue>
-
-#include "hal/hci_hal.h"
-#include "hal/hci_hal_host.h"
-#include "hal/snoop_logger.h"
-#include "main/shim/entry.h"
-#include "os/reactor.h"
-#include "os/thread.h"
-
-namespace {
-constexpr int INVALID_FD = -1;
-
-constexpr uint8_t kH4Command = 0x01;
-constexpr uint8_t kH4Acl = 0x02;
-constexpr uint8_t kH4Sco = 0x03;
-constexpr uint8_t kH4Event = 0x04;
-constexpr uint8_t kH4Iso = 0x05;
-
-constexpr uint8_t kH4HeaderSize = 1;
-constexpr uint8_t kHciAclHeaderSize = 4;
-constexpr uint8_t kHciScoHeaderSize = 3;
-constexpr uint8_t kHciEvtHeaderSize = 2;
-constexpr uint8_t kHciIsoHeaderSize = 4;
-constexpr int kBufSize =
-        1024 + 4 + 1;  // DeviceProperties::acl_data_packet_size_ + ACL header + H4 header
-
-int ConnectToSocket() {
-  auto* config = bluetooth::hal::HciHalHostRootcanalConfig::Get();
-  const std::string& server = config->GetServerAddress();
-  int port = config->GetPort();
-
-  int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
-  if (socket_fd < 1) {
-    bluetooth::log::error("can't create socket: {}", strerror(errno));
-    return INVALID_FD;
-  }
-
-  struct hostent* host;
-  host = gethostbyname(server.c_str());
-  if (host == nullptr) {
-    bluetooth::log::error("can't get server name");
-    return INVALID_FD;
-  }
-
-  struct sockaddr_in serv_addr;
-  memset((void*)&serv_addr, 0, sizeof(serv_addr));
-  serv_addr.sin_family = AF_INET;
-  serv_addr.sin_addr.s_addr = INADDR_ANY;
-  serv_addr.sin_port = htons(port);
-
-  int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
-  if (result < 0) {
-    bluetooth::log::error("can't connect: {}", strerror(errno));
-    return INVALID_FD;
-  }
-
-  timeval socket_timeout{
-          .tv_sec = 3,
-          .tv_usec = 0,
-  };
-  int ret = setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &socket_timeout, sizeof(socket_timeout));
-  if (ret == -1) {
-    bluetooth::log::error("can't control socket fd: {}", strerror(errno));
-    return INVALID_FD;
-  }
-  return socket_fd;
-}
-}  // namespace
-
-namespace bluetooth {
-namespace hal {
-
-class HciHalHost : public HciHal {
-public:
-  void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    log::info("before");
-    {
-      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
-      log::assert_that(
-              incoming_packet_callback_ == nullptr && callback != nullptr,
-              "assert failed: incoming_packet_callback_ == nullptr && callback != nullptr");
-      incoming_packet_callback_ = callback;
-    }
-    log::info("after");
-  }
-
-  void unregisterIncomingPacketCallback() override {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    log::info("before");
-    {
-      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
-      incoming_packet_callback_ = nullptr;
-    }
-    log::info("after");
-  }
-
-  void sendHciCommand(HciPacket command) override {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
-    std::vector<uint8_t> packet = std::move(command);
-    if (btsnoop_logger_) {
-      btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
-                               SnoopLogger::PacketType::CMD);
-    }
-    packet.insert(packet.cbegin(), kH4Command);
-    write_to_fd(packet);
-  }
-
-  void sendAclData(HciPacket data) override {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
-    std::vector<uint8_t> packet = std::move(data);
-    if (btsnoop_logger_) {
-      btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
-                               SnoopLogger::PacketType::ACL);
-    }
-    packet.insert(packet.cbegin(), kH4Acl);
-    write_to_fd(packet);
-  }
-
-  void sendScoData(HciPacket data) override {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
-    std::vector<uint8_t> packet = std::move(data);
-    if (btsnoop_logger_) {
-      btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
-                               SnoopLogger::PacketType::SCO);
-    }
-    packet.insert(packet.cbegin(), kH4Sco);
-    write_to_fd(packet);
-  }
-
-  void sendIsoData(HciPacket data) override {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
-    std::vector<uint8_t> packet = std::move(data);
-    if (btsnoop_logger_) {
-      btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
-                               SnoopLogger::PacketType::ISO);
-    }
-    packet.insert(packet.cbegin(), kH4Iso);
-    write_to_fd(packet);
-  }
-
-protected:
-  void ListDependencies(ModuleList* /*list*/) const {}
-
-  void Start() override {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    log::assert_that(sock_fd_ == INVALID_FD, "assert failed: sock_fd_ == INVALID_FD");
-    sock_fd_ = ConnectToSocket();
-    log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
-    reactable_ = hci_incoming_thread_.GetReactor()->Register(
-            sock_fd_, 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);
-    btsnoop_logger_ = shim::GetSnoopLogger();
-    log::info("HAL opened successfully");
-  }
-
-  void Stop() override {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    log::info("HAL is closing");
-    if (reactable_ != nullptr) {
-      hci_incoming_thread_.GetReactor()->Unregister(reactable_);
-      log::info("HAL is stopping, start waiting for last callback");
-      // Wait up to 1 second for the last incoming packet callback to finish
-      hci_incoming_thread_.GetReactor()->WaitForUnregisteredReactable(
-              std::chrono::milliseconds(1000));
-      log::info("HAL is stopping, finished waiting for last callback");
-      log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
-    }
-    reactable_ = nullptr;
-    {
-      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
-      incoming_packet_callback_ = nullptr;
-    }
-    ::close(sock_fd_);
-    sock_fd_ = INVALID_FD;
-    log::info("HAL is closed");
-  }
-
-  std::string ToString() const override { return std::string("HciHalHost"); }
-
-private:
-  // Held when APIs are called, NOT to be held during callbacks
-  std::mutex api_mutex_;
-  HciHalCallbacks* incoming_packet_callback_ = nullptr;
-  std::mutex incoming_packet_callback_mutex_;
-  int sock_fd_ = INVALID_FD;
-  bluetooth::os::Thread hci_incoming_thread_ =
-          bluetooth::os::Thread("hci_incoming_thread", bluetooth::os::Thread::Priority::NORMAL);
-  bluetooth::os::Reactor::Reactable* reactable_ = nullptr;
-  std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
-  SnoopLogger* btsnoop_logger_ = nullptr;
-
-  void write_to_fd(HciPacket packet) {
-    // TODO: replace this with new queue when it's ready
-    hci_outgoing_queue_.emplace(packet);
-    if (hci_outgoing_queue_.size() == 1) {
-      hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_,
-                                                            os::Reactor::REACT_ON_READ_WRITE);
-    }
-  }
-
-  void send_packet_ready() {
-    std::lock_guard<std::mutex> lock(api_mutex_);
-    if (hci_outgoing_queue_.empty()) {
-      return;
-    }
-    auto packet_to_send = hci_outgoing_queue_.front();
-    auto bytes_written = write(sock_fd_, (void*)packet_to_send.data(), packet_to_send.size());
-    hci_outgoing_queue_.pop();
-    if (bytes_written == -1) {
-      abort();
-    }
-    if (hci_outgoing_queue_.empty()) {
-      hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_,
-                                                            os::Reactor::REACT_ON_READ_ONLY);
-    }
-  }
-
-  bool socketRecvAll(void* buffer, int bufferLen) {
-    auto buf = static_cast<char*>(buffer);
-    while (bufferLen > 0) {
-      ssize_t ret;
-      RUN_NO_INTR(ret = recv(sock_fd_, buf, bufferLen, 0));
-      if (ret <= 0) {
-        return false;
-      }
-      buf += ret;
-      bufferLen -= ret;
-    }
-    return true;
-  }
-
-  void incoming_packet_received() {
-    {
-      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
-      if (incoming_packet_callback_ == nullptr) {
-        log::info("Dropping a packet");
-        return;
-      }
-    }
-    uint8_t buf[kBufSize] = {};
-
-    ssize_t received_size;
-    RUN_NO_INTR(received_size = recv(sock_fd_, buf, kH4HeaderSize, 0));
-    log::assert_that(received_size != -1, "Can't receive from socket: {}", strerror(errno));
-    if (received_size == 0) {
-      log::warn("Can't read H4 header. EOF received");
-      raise(SIGINT);
-      return;
-    }
-
-    if (buf[0] == kH4Event) {
-      log::assert_that(socketRecvAll(buf + kH4HeaderSize, kHciEvtHeaderSize),
-                       "Can't receive from socket: {}", strerror(errno));
-
-      uint8_t hci_evt_parameter_total_length = buf[2];
-      log::assert_that(socketRecvAll(buf + kH4HeaderSize + kHciEvtHeaderSize,
-                                     hci_evt_parameter_total_length),
-                       "Can't receive from socket: {}", strerror(errno));
-
-      HciPacket receivedHciPacket;
-      receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciEvtHeaderSize +
-                                                            hci_evt_parameter_total_length);
-      if (btsnoop_logger_) {
-        btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
-                                 SnoopLogger::PacketType::EVT);
-      }
-      {
-        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
-        if (incoming_packet_callback_ == nullptr) {
-          log::info("Dropping an event after processing");
-          return;
-        }
-        incoming_packet_callback_->hciEventReceived(receivedHciPacket);
-      }
-    }
-
-    if (buf[0] == kH4Acl) {
-      log::assert_that(socketRecvAll(buf + kH4HeaderSize, kHciAclHeaderSize),
-                       "Can't receive from socket: {}", strerror(errno));
-
-      uint16_t hci_acl_data_total_length = (buf[4] << 8) + buf[3];
-      log::assert_that(
-              socketRecvAll(buf + kH4HeaderSize + kHciAclHeaderSize, hci_acl_data_total_length),
-              "Can't receive from socket: {}", strerror(errno));
-
-      HciPacket receivedHciPacket;
-      receivedHciPacket.assign(buf + kH4HeaderSize,
-                               buf + kH4HeaderSize + kHciAclHeaderSize + hci_acl_data_total_length);
-      if (btsnoop_logger_) {
-        btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
-                                 SnoopLogger::PacketType::ACL);
-      }
-      {
-        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
-        if (incoming_packet_callback_ == nullptr) {
-          log::info("Dropping an ACL packet after processing");
-          return;
-        }
-        incoming_packet_callback_->aclDataReceived(receivedHciPacket);
-      }
-    }
-
-    if (buf[0] == kH4Sco) {
-      log::assert_that(socketRecvAll(buf + kH4HeaderSize, kHciScoHeaderSize),
-                       "Can't receive from socket: {}", strerror(errno));
-
-      uint8_t hci_sco_data_total_length = buf[3];
-      log::assert_that(
-              socketRecvAll(buf + kH4HeaderSize + kHciScoHeaderSize, hci_sco_data_total_length),
-              "Can't receive from socket: {}", strerror(errno));
-
-      HciPacket receivedHciPacket;
-      receivedHciPacket.assign(buf + kH4HeaderSize,
-                               buf + kH4HeaderSize + kHciScoHeaderSize + hci_sco_data_total_length);
-      if (btsnoop_logger_) {
-        btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
-                                 SnoopLogger::PacketType::SCO);
-      }
-      {
-        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
-        if (incoming_packet_callback_ == nullptr) {
-          log::info("Dropping a SCO packet after processing");
-          return;
-        }
-        incoming_packet_callback_->scoDataReceived(receivedHciPacket);
-      }
-    }
-
-    if (buf[0] == kH4Iso) {
-      log::assert_that(socketRecvAll(buf + kH4HeaderSize, kHciIsoHeaderSize),
-                       "Can't receive from socket: {}", strerror(errno));
-
-      uint16_t hci_iso_data_total_length = ((buf[4] & 0x3f) << 8) + buf[3];
-      log::assert_that(
-              socketRecvAll(buf + kH4HeaderSize + kHciIsoHeaderSize, hci_iso_data_total_length),
-              "Can't receive from socket: {}", strerror(errno));
-
-      HciPacket receivedHciPacket;
-      receivedHciPacket.assign(buf + kH4HeaderSize,
-                               buf + kH4HeaderSize + kHciIsoHeaderSize + hci_iso_data_total_length);
-      if (btsnoop_logger_) {
-        btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
-                                 SnoopLogger::PacketType::ISO);
-      }
-      {
-        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
-        if (incoming_packet_callback_ == nullptr) {
-          log::info("Dropping a ISO packet after processing");
-          return;
-        }
-        incoming_packet_callback_->isoDataReceived(receivedHciPacket);
-      }
-    }
-    memset(buf, 0, kBufSize);
-  }
-};
-
-const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHost(); });
-
-}  // namespace hal
-}  // namespace bluetooth
diff --git a/system/gd/hal/hci_hal_host.h b/system/gd/hal/hci_hal_host_rootcanal_config.h
similarity index 100%
rename from system/gd/hal/hci_hal_host.h
rename to system/gd/hal/hci_hal_host_rootcanal_config.h
diff --git a/system/gd/hal/hci_hal_impl.h b/system/gd/hal/hci_hal_impl.h
new file mode 100644
index 0000000..8c081ce
--- /dev/null
+++ b/system/gd/hal/hci_hal_impl.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef __ANDROID__
+#include "hal/hci_hal_impl_android.h"
+#else
+#include "hal/hci_hal_impl_host.h"
+#endif
diff --git a/system/gd/hal/hci_hal_android.cc b/system/gd/hal/hci_hal_impl_android.cc
similarity index 61%
rename from system/gd/hal/hci_hal_android.cc
rename to system/gd/hal/hci_hal_impl_android.cc
index 4ac4fc8..9230f99 100644
--- a/system/gd/hal/hci_hal_android.cc
+++ b/system/gd/hal/hci_hal_impl_android.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "hci_hal_impl_android.h"
+
 #include <android_bluetooth_sysprop.h>
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
@@ -129,89 +131,66 @@
   SnoopLogger* btsnoop_logger_;
 };
 
-class HciHalImpl : public HciHal {
-public:
-  void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
-    callbacks_->SetCallback(callback);
+void HciHalImpl::registerIncomingPacketCallback(HciHalCallbacks* callback) {
+  callbacks_->SetCallback(callback);
+}
+
+void HciHalImpl::unregisterIncomingPacketCallback() { callbacks_->ResetCallback(); }
+
+void HciHalImpl::sendHciCommand(HciPacket packet) {
+  btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
+  backend_->sendHciCommand(packet);
+}
+
+void HciHalImpl::sendAclData(HciPacket packet) {
+  btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
+  backend_->sendAclData(packet);
+}
+
+void HciHalImpl::sendScoData(HciPacket packet) {
+  btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
+  backend_->sendScoData(packet);
+}
+
+void HciHalImpl::sendIsoData(HciPacket packet) {
+  btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ISO);
+  backend_->sendIsoData(packet);
+}
+
+uint16_t HciHalImpl::getMsftOpcode() {
+  if (com::android::bluetooth::flags::le_scan_msft_support()) {
+    return android::sysprop::bluetooth::Hci::msft_vendor_opcode().value_or(0);
+  }
+  return 0;
+}
+
+HciHalImpl::HciHalImpl(os::Handler* handler, LinkClocker* link_clocker, SnoopLogger* btsnoop_logger)
+    : link_clocker_(link_clocker), btsnoop_logger_(btsnoop_logger) {
+  common::StopWatch stop_watch(__func__);
+  log::assert_that(backend_ == nullptr,
+                   "Start can't be called more than once before Stop is called.");
+
+  if (com::android::bluetooth::flags::hci_instance_name_use_injected()) {
+    backend_ = HciBackend::CreateAidl(bluetooth::os::ParameterProvider::GetHciInstanceName());
+  } else {
+    backend_ = HciBackend::CreateAidl();
+  }
+  if (!backend_) {
+    backend_ = HciBackend::CreateHidl(handler);
   }
 
-  void unregisterIncomingPacketCallback() override { callbacks_->ResetCallback(); }
+  log::assert_that(backend_ != nullptr, "No backend available");
 
-  void sendHciCommand(HciPacket packet) override {
-    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
-                             SnoopLogger::PacketType::CMD);
-    backend_->sendHciCommand(packet);
-  }
+  callbacks_ = std::make_shared<HciCallbacksImpl>(btsnoop_logger_, link_clocker_);
 
-  void sendAclData(HciPacket packet) override {
-    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
-                             SnoopLogger::PacketType::ACL);
-    backend_->sendAclData(packet);
-  }
+  backend_->initialize(callbacks_);
+  callbacks_->init_promise->get_future().wait();
+}
 
-  void sendScoData(HciPacket packet) override {
-    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
-                             SnoopLogger::PacketType::SCO);
-    backend_->sendScoData(packet);
-  }
-
-  void sendIsoData(HciPacket packet) override {
-    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
-                             SnoopLogger::PacketType::ISO);
-    backend_->sendIsoData(packet);
-  }
-
-  uint16_t getMsftOpcode() override {
-    if (com::android::bluetooth::flags::le_scan_msft_support()) {
-      return android::sysprop::bluetooth::Hci::msft_vendor_opcode().value_or(0);
-    }
-    return 0;
-  }
-
-protected:
-  void ListDependencies(ModuleList* list) const override { list->add<LinkClocker>(); }
-
-  void Start() override {
-    common::StopWatch stop_watch(__func__);
-    log::assert_that(backend_ == nullptr,
-                     "Start can't be called more than once before Stop is called.");
-
-    link_clocker_ = GetDependency<LinkClocker>();
-    btsnoop_logger_ = shim::GetSnoopLogger();
-
-    if (com::android::bluetooth::flags::hci_instance_name_use_injected()) {
-      backend_ = HciBackend::CreateAidl(bluetooth::os::ParameterProvider::GetHciInstanceName());
-    } else {
-      backend_ = HciBackend::CreateAidl();
-    }
-    if (!backend_) {
-      backend_ = HciBackend::CreateHidl(GetHandler());
-    }
-
-    log::assert_that(backend_ != nullptr, "No backend available");
-
-    callbacks_ = std::make_shared<HciCallbacksImpl>(btsnoop_logger_, link_clocker_);
-
-    backend_->initialize(callbacks_);
-    callbacks_->init_promise->get_future().wait();
-  }
-
-  void Stop() override {
-    backend_.reset();
-    callbacks_.reset();
-    btsnoop_logger_ = nullptr;
-    link_clocker_ = nullptr;
-  }
-
-  std::string ToString() const override { return std::string("HciHal"); }
-
-private:
-  std::shared_ptr<HciCallbacksImpl> callbacks_;
-  std::shared_ptr<HciBackend> backend_;
-  SnoopLogger* btsnoop_logger_ = nullptr;
-  LinkClocker* link_clocker_ = nullptr;
-};
-
-const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalImpl(); });
-
+HciHalImpl::~HciHalImpl() {
+  backend_.reset();
+  callbacks_.reset();
+  btsnoop_logger_ = nullptr;
+  link_clocker_ = nullptr;
+}
 }  // namespace bluetooth::hal
diff --git a/system/gd/hal/hci_hal_impl_android.h b/system/gd/hal/hci_hal_impl_android.h
new file mode 100644
index 0000000..e5a52cb
--- /dev/null
+++ b/system/gd/hal/hci_hal_impl_android.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "hal/hci_backend.h"
+#include "hal/hci_hal.h"
+#include "hal/link_clocker.h"
+#include "hal/snoop_logger.h"
+
+namespace bluetooth::hal {
+class HciCallbacksImpl;
+
+class HciHalImpl : public HciHal {
+public:
+  HciHalImpl(os::Handler* handler, LinkClocker* link_clocker, SnoopLogger* btsnoop_logger);
+  ~HciHalImpl();
+
+  void sendHciCommand(HciPacket packet) override;
+  void sendAclData(HciPacket packet) override;
+  void sendScoData(HciPacket packet) override;
+  void sendIsoData(HciPacket packet) override;
+
+  void registerIncomingPacketCallback(HciHalCallbacks* callback) override;
+  void unregisterIncomingPacketCallback() override;
+  uint16_t getMsftOpcode() override;
+
+private:
+  std::shared_ptr<HciCallbacksImpl> callbacks_;
+  std::shared_ptr<HciBackend> backend_;
+  LinkClocker* link_clocker_ = nullptr;
+  SnoopLogger* btsnoop_logger_ = nullptr;
+};
+
+}  // namespace bluetooth::hal
diff --git a/system/gd/hal/hci_hal_android_test.cc b/system/gd/hal/hci_hal_impl_android_test.cc
similarity index 89%
rename from system/gd/hal/hci_hal_android_test.cc
rename to system/gd/hal/hci_hal_impl_android_test.cc
index a43b0ec..690b15e 100644
--- a/system/gd/hal/hci_hal_android_test.cc
+++ b/system/gd/hal/hci_hal_impl_android_test.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "hal/hci_hal_impl_android.h"
+
 #include <gtest/gtest.h>
 
 #include <chrono>
@@ -22,7 +24,8 @@
 
 #include "com_android_bluetooth_flags.h"
 #include "hal/hci_backend.h"
-#include "hal/hci_hal.h"
+#include "hal/link_clocker.h"
+#include "module.h"  // FakeRegistry
 #include "os/thread.h"
 
 using ::bluetooth::os::Thread;
@@ -81,7 +84,9 @@
   void SetUp() override {
     thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
     handler_ = new os::Handler(thread_);
-    hal = fake_registry_.Start<HciHal>(thread_, handler_);
+
+    link_clocker = std::make_unique<LinkClocker>();
+    hal = std::make_unique<HciHalImpl>(handler_, link_clocker.get(), nullptr /* snoop_logger */);
   }
 
   void TearDown() override {
@@ -89,12 +94,14 @@
     if (com::android::bluetooth::flags::same_handler_for_all_modules()) {
       handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout);
     }
-    fake_registry_.StopAll();
+    hal.reset();
+    link_clocker.reset();
     delete handler_;
     delete thread_;
   }
 
-  HciHal* hal;
+  std::unique_ptr<LinkClocker> link_clocker;
+  std::unique_ptr<HciHal> hal;
 
 private:
   ModuleRegistry fake_registry_;
diff --git a/system/gd/hal/hci_hal_host.cc b/system/gd/hal/hci_hal_impl_host.cc
similarity index 96%
rename from system/gd/hal/hci_hal_host.cc
rename to system/gd/hal/hci_hal_impl_host.cc
index 4976258..a0ea0e5 100644
--- a/system/gd/hal/hci_hal_host.cc
+++ b/system/gd/hal/hci_hal_impl_host.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "hal/hci_hal_host.h"
+#include "hal/hci_hal_impl_host.h"
 
 #include <bluetooth/log.h>
 #include <netdb.h>
@@ -234,7 +234,7 @@
 namespace bluetooth {
 namespace hal {
 
-class HciHalHost : public HciHal {
+class HciHalImpl : public HciHal {
 public:
   void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
     std::lock_guard<std::mutex> lock(api_mutex_);
@@ -308,10 +308,8 @@
     return os::Management::getInstance().getVendorSpecificCode(MGMT_VS_OPCODE_MSFT);
   }
 
-protected:
-  void ListDependencies(ModuleList* list) const { list->add<LinkClocker>(); }
-
-  void Start() override {
+  HciHalImpl(LinkClocker* link_clocker, SnoopLogger* snoop_logger)
+      : link_clocker_(link_clocker), btsnoop_logger_(snoop_logger) {
     std::lock_guard<std::mutex> lock(api_mutex_);
     log::assert_that(sock_fd_ == INVALID_FD, "assert failed: sock_fd_ == INVALID_FD");
     sock_fd_ = ConnectToSocket();
@@ -324,16 +322,17 @@
     }
 
     reactable_ = hci_incoming_thread_.GetReactor()->Register(
-            sock_fd_, common::Bind(&HciHalHost::incoming_packet_received, common::Unretained(this)),
-            common::Bind(&HciHalHost::send_packet_ready, common::Unretained(this)));
+            sock_fd_, common::Bind(&HciHalImpl::incoming_packet_received, common::Unretained(this)),
+            common::Bind(&HciHalImpl::send_packet_ready, common::Unretained(this)));
     hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_,
                                                           os::Reactor::REACT_ON_READ_ONLY);
-    link_clocker_ = GetDependency<LinkClocker>();
-    btsnoop_logger_ = shim::GetSnoopLogger();
     log::info("HAL opened successfully");
   }
 
-  void Stop() override {
+  HciHalImpl(os::Handler*, LinkClocker* link_clocker, SnoopLogger* btsnoop_logger)
+      : HciHalImpl(link_clocker, btsnoop_logger) {}
+
+  ~HciHalImpl() {
     std::lock_guard<std::mutex> lock(api_mutex_);
     log::info("HAL is closing");
     if (reactable_ != nullptr) {
@@ -359,8 +358,6 @@
     log::info("HAL is closed");
   }
 
-  std::string ToString() const override { return std::string("HciHalHost"); }
-
 private:
   // Held when APIs are called, NOT to be held during callbacks
   std::mutex api_mutex_;
@@ -371,8 +368,8 @@
           bluetooth::os::Thread("hci_incoming_thread", bluetooth::os::Thread::Priority::NORMAL);
   bluetooth::os::Reactor::Reactable* reactable_ = nullptr;
   std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
-  SnoopLogger* btsnoop_logger_ = nullptr;
   LinkClocker* link_clocker_ = nullptr;
+  SnoopLogger* btsnoop_logger_ = nullptr;
 
   void write_to_fd(HciPacket packet) {
     // TODO(chromeos-bt-team@): replace this with new queue when it's ready
@@ -531,7 +528,5 @@
   }
 };
 
-const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHost(); });
-
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/system/gd/hal/hci_hal_impl_host.h b/system/gd/hal/hci_hal_impl_host.h
new file mode 100644
index 0000000..4ed8cd7
--- /dev/null
+++ b/system/gd/hal/hci_hal_impl_host.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <queue>
+
+#include "hal/hci_hal.h"
+#include "hal/link_clocker.h"
+#include "hal/snoop_logger.h"
+#include "main/shim/entry.h"
+#include "os/reactor.h"
+#include "os/thread.h"
+
+namespace bluetooth {
+namespace hal {
+
+namespace {
+constexpr int INVALID_FD = -1;
+}
+
+class HciHalImpl : public HciHal {
+public:
+  HciHalImpl(os::Handler*, LinkClocker*, SnoopLogger*) : HciHalImpl() {}
+  HciHalImpl();
+  ~HciHalImpl();
+
+  void registerIncomingPacketCallback(HciHalCallbacks* callback) override;
+  void unregisterIncomingPacketCallback() override;
+  void sendHciCommand(HciPacket command) override;
+  void sendAclData(HciPacket data) override;
+  void sendScoData(HciPacket data) override;
+  void sendIsoData(HciPacket data) override;
+
+private:
+  // Held when APIs are called, NOT to be held during callbacks
+  std::mutex api_mutex_;
+  HciHalCallbacks* incoming_packet_callback_ = nullptr;
+  std::mutex incoming_packet_callback_mutex_;
+  int sock_fd_ = INVALID_FD;
+  bluetooth::os::Thread hci_incoming_thread_ =
+          bluetooth::os::Thread("hci_incoming_thread", bluetooth::os::Thread::Priority::NORMAL);
+  bluetooth::os::Reactor::Reactable* reactable_ = nullptr;
+  std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
+  SnoopLogger* btsnoop_logger_ = nullptr;
+
+  void write_to_fd(HciPacket packet);
+  void send_packet_ready();
+  bool socketRecvAll(void* buffer, int bufferLen);
+  void incoming_packet_received();
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/system/gd/hal/hci_hal_impl_host_rootcanal.cc b/system/gd/hal/hci_hal_impl_host_rootcanal.cc
new file mode 100644
index 0000000..39c2ad9
--- /dev/null
+++ b/system/gd/hal/hci_hal_impl_host_rootcanal.cc
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hal/hci_hal_impl_host.h"
+
+#include <bluetooth/log.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <csignal>
+#include <mutex>
+#include <queue>
+
+#include "hal/hci_hal.h"
+#include "hal/hci_hal_host_rootcanal_config.h"
+#include "hal/snoop_logger.h"
+#include "main/shim/entry.h"
+#include "os/reactor.h"
+#include "os/thread.h"
+
+namespace {
+constexpr int INVALID_FD = -1;
+
+constexpr uint8_t kH4Command = 0x01;
+constexpr uint8_t kH4Acl = 0x02;
+constexpr uint8_t kH4Sco = 0x03;
+constexpr uint8_t kH4Event = 0x04;
+constexpr uint8_t kH4Iso = 0x05;
+
+constexpr uint8_t kH4HeaderSize = 1;
+constexpr uint8_t kHciAclHeaderSize = 4;
+constexpr uint8_t kHciScoHeaderSize = 3;
+constexpr uint8_t kHciEvtHeaderSize = 2;
+constexpr uint8_t kHciIsoHeaderSize = 4;
+constexpr int kBufSize =
+        1024 + 4 + 1;  // DeviceProperties::acl_data_packet_size_ + ACL header + H4 header
+
+int ConnectToSocket() {
+  auto* config = bluetooth::hal::HciHalHostRootcanalConfig::Get();
+  const std::string& server = config->GetServerAddress();
+  int port = config->GetPort();
+
+  int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+  if (socket_fd < 1) {
+    bluetooth::log::error("can't create socket: {}", strerror(errno));
+    return INVALID_FD;
+  }
+
+  struct hostent* host;
+  host = gethostbyname(server.c_str());
+  if (host == nullptr) {
+    bluetooth::log::error("can't get server name");
+    return INVALID_FD;
+  }
+
+  struct sockaddr_in serv_addr;
+  memset((void*)&serv_addr, 0, sizeof(serv_addr));
+  serv_addr.sin_family = AF_INET;
+  serv_addr.sin_addr.s_addr = INADDR_ANY;
+  serv_addr.sin_port = htons(port);
+
+  int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+  if (result < 0) {
+    bluetooth::log::error("can't connect: {}", strerror(errno));
+    return INVALID_FD;
+  }
+
+  timeval socket_timeout{
+          .tv_sec = 3,
+          .tv_usec = 0,
+  };
+  int ret = setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &socket_timeout, sizeof(socket_timeout));
+  if (ret == -1) {
+    bluetooth::log::error("can't control socket fd: {}", strerror(errno));
+    return INVALID_FD;
+  }
+  return socket_fd;
+}
+}  // namespace
+
+namespace bluetooth {
+namespace hal {
+
+void HciHalImpl::registerIncomingPacketCallback(HciHalCallbacks* callback) {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  log::info("before");
+  {
+    std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+    log::assert_that(incoming_packet_callback_ == nullptr && callback != nullptr,
+                     "assert failed: incoming_packet_callback_ == nullptr && callback != nullptr");
+    incoming_packet_callback_ = callback;
+  }
+  log::info("after");
+}
+
+void HciHalImpl::unregisterIncomingPacketCallback() {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  log::info("before");
+  {
+    std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+    incoming_packet_callback_ = nullptr;
+  }
+  log::info("after");
+}
+
+void HciHalImpl::sendHciCommand(HciPacket command) {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
+  std::vector<uint8_t> packet = std::move(command);
+  if (btsnoop_logger_) {
+    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
+                             SnoopLogger::PacketType::CMD);
+  }
+  packet.insert(packet.cbegin(), kH4Command);
+  write_to_fd(packet);
+}
+
+void HciHalImpl::sendAclData(HciPacket data) {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
+  std::vector<uint8_t> packet = std::move(data);
+  if (btsnoop_logger_) {
+    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
+                             SnoopLogger::PacketType::ACL);
+  }
+  packet.insert(packet.cbegin(), kH4Acl);
+  write_to_fd(packet);
+}
+
+void HciHalImpl::sendScoData(HciPacket data) {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
+  std::vector<uint8_t> packet = std::move(data);
+  if (btsnoop_logger_) {
+    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
+                             SnoopLogger::PacketType::SCO);
+  }
+  packet.insert(packet.cbegin(), kH4Sco);
+  write_to_fd(packet);
+}
+
+void HciHalImpl::sendIsoData(HciPacket data) {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
+  std::vector<uint8_t> packet = std::move(data);
+  if (btsnoop_logger_) {
+    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING,
+                             SnoopLogger::PacketType::ISO);
+  }
+  packet.insert(packet.cbegin(), kH4Iso);
+  write_to_fd(packet);
+}
+
+HciHalImpl::HciHalImpl() {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  log::assert_that(sock_fd_ == INVALID_FD, "assert failed: sock_fd_ == INVALID_FD");
+  sock_fd_ = ConnectToSocket();
+  log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
+  reactable_ = hci_incoming_thread_.GetReactor()->Register(
+          sock_fd_, common::Bind(&HciHalImpl::incoming_packet_received, common::Unretained(this)),
+          common::Bind(&HciHalImpl::send_packet_ready, common::Unretained(this)));
+  hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_,
+                                                        os::Reactor::REACT_ON_READ_ONLY);
+  btsnoop_logger_ = shim::GetSnoopLogger();
+  log::info("HAL opened successfully");
+}
+
+HciHalImpl::~HciHalImpl() {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  log::info("HAL is closing");
+  if (reactable_ != nullptr) {
+    hci_incoming_thread_.GetReactor()->Unregister(reactable_);
+    log::info("HAL is stopping, start waiting for last callback");
+    // Wait up to 1 second for the last incoming packet callback to finish
+    hci_incoming_thread_.GetReactor()->WaitForUnregisteredReactable(
+            std::chrono::milliseconds(1000));
+    log::info("HAL is stopping, finished waiting for last callback");
+    log::assert_that(sock_fd_ != INVALID_FD, "assert failed: sock_fd_ != INVALID_FD");
+  }
+  reactable_ = nullptr;
+  {
+    std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+    incoming_packet_callback_ = nullptr;
+  }
+  ::close(sock_fd_);
+  sock_fd_ = INVALID_FD;
+  log::info("HAL is closed");
+}
+
+void HciHalImpl::write_to_fd(HciPacket packet) {
+  // TODO: replace this with new queue when it's ready
+  hci_outgoing_queue_.emplace(packet);
+  if (hci_outgoing_queue_.size() == 1) {
+    hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_,
+                                                          os::Reactor::REACT_ON_READ_WRITE);
+  }
+}
+
+void HciHalImpl::send_packet_ready() {
+  std::lock_guard<std::mutex> lock(api_mutex_);
+  if (hci_outgoing_queue_.empty()) {
+    return;
+  }
+  auto packet_to_send = hci_outgoing_queue_.front();
+  auto bytes_written = write(sock_fd_, (void*)packet_to_send.data(), packet_to_send.size());
+  hci_outgoing_queue_.pop();
+  if (bytes_written == -1) {
+    abort();
+  }
+  if (hci_outgoing_queue_.empty()) {
+    hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_,
+                                                          os::Reactor::REACT_ON_READ_ONLY);
+  }
+}
+
+bool HciHalImpl::socketRecvAll(void* buffer, int bufferLen) {
+  auto buf = static_cast<char*>(buffer);
+  while (bufferLen > 0) {
+    ssize_t ret;
+    RUN_NO_INTR(ret = recv(sock_fd_, buf, bufferLen, 0));
+    if (ret <= 0) {
+      return false;
+    }
+    buf += ret;
+    bufferLen -= ret;
+  }
+  return true;
+}
+
+void HciHalImpl::incoming_packet_received() {
+  {
+    std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+    if (incoming_packet_callback_ == nullptr) {
+      log::info("Dropping a packet");
+      return;
+    }
+  }
+  uint8_t buf[kBufSize] = {};
+
+  ssize_t received_size;
+  RUN_NO_INTR(received_size = recv(sock_fd_, buf, kH4HeaderSize, 0));
+  log::assert_that(received_size != -1, "Can't receive from socket: {}", strerror(errno));
+  if (received_size == 0) {
+    log::warn("Can't read H4 header. EOF received");
+    raise(SIGINT);
+    return;
+  }
+
+  if (buf[0] == kH4Event) {
+    log::assert_that(socketRecvAll(buf + kH4HeaderSize, kHciEvtHeaderSize),
+                     "Can't receive from socket: {}", strerror(errno));
+
+    uint8_t hci_evt_parameter_total_length = buf[2];
+    log::assert_that(
+            socketRecvAll(buf + kH4HeaderSize + kHciEvtHeaderSize, hci_evt_parameter_total_length),
+            "Can't receive from socket: {}", strerror(errno));
+
+    HciPacket receivedHciPacket;
+    receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciEvtHeaderSize +
+                                                          hci_evt_parameter_total_length);
+    if (btsnoop_logger_) {
+      btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
+                               SnoopLogger::PacketType::EVT);
+    }
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      if (incoming_packet_callback_ == nullptr) {
+        log::info("Dropping an event after processing");
+        return;
+      }
+      incoming_packet_callback_->hciEventReceived(receivedHciPacket);
+    }
+  }
+
+  if (buf[0] == kH4Acl) {
+    log::assert_that(socketRecvAll(buf + kH4HeaderSize, kHciAclHeaderSize),
+                     "Can't receive from socket: {}", strerror(errno));
+
+    uint16_t hci_acl_data_total_length = (buf[4] << 8) + buf[3];
+    log::assert_that(
+            socketRecvAll(buf + kH4HeaderSize + kHciAclHeaderSize, hci_acl_data_total_length),
+            "Can't receive from socket: {}", strerror(errno));
+
+    HciPacket receivedHciPacket;
+    receivedHciPacket.assign(buf + kH4HeaderSize,
+                             buf + kH4HeaderSize + kHciAclHeaderSize + hci_acl_data_total_length);
+    if (btsnoop_logger_) {
+      btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
+                               SnoopLogger::PacketType::ACL);
+    }
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      if (incoming_packet_callback_ == nullptr) {
+        log::info("Dropping an ACL packet after processing");
+        return;
+      }
+      incoming_packet_callback_->aclDataReceived(receivedHciPacket);
+    }
+  }
+
+  if (buf[0] == kH4Sco) {
+    log::assert_that(socketRecvAll(buf + kH4HeaderSize, kHciScoHeaderSize),
+                     "Can't receive from socket: {}", strerror(errno));
+
+    uint8_t hci_sco_data_total_length = buf[3];
+    log::assert_that(
+            socketRecvAll(buf + kH4HeaderSize + kHciScoHeaderSize, hci_sco_data_total_length),
+            "Can't receive from socket: {}", strerror(errno));
+
+    HciPacket receivedHciPacket;
+    receivedHciPacket.assign(buf + kH4HeaderSize,
+                             buf + kH4HeaderSize + kHciScoHeaderSize + hci_sco_data_total_length);
+    if (btsnoop_logger_) {
+      btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
+                               SnoopLogger::PacketType::SCO);
+    }
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      if (incoming_packet_callback_ == nullptr) {
+        log::info("Dropping a SCO packet after processing");
+        return;
+      }
+      incoming_packet_callback_->scoDataReceived(receivedHciPacket);
+    }
+  }
+
+  if (buf[0] == kH4Iso) {
+    log::assert_that(socketRecvAll(buf + kH4HeaderSize, kHciIsoHeaderSize),
+                     "Can't receive from socket: {}", strerror(errno));
+
+    uint16_t hci_iso_data_total_length = ((buf[4] & 0x3f) << 8) + buf[3];
+    log::assert_that(
+            socketRecvAll(buf + kH4HeaderSize + kHciIsoHeaderSize, hci_iso_data_total_length),
+            "Can't receive from socket: {}", strerror(errno));
+
+    HciPacket receivedHciPacket;
+    receivedHciPacket.assign(buf + kH4HeaderSize,
+                             buf + kH4HeaderSize + kHciIsoHeaderSize + hci_iso_data_total_length);
+    if (btsnoop_logger_) {
+      btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING,
+                               SnoopLogger::PacketType::ISO);
+    }
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      if (incoming_packet_callback_ == nullptr) {
+        log::info("Dropping a ISO packet after processing");
+        return;
+      }
+      incoming_packet_callback_->isoDataReceived(receivedHciPacket);
+    }
+  }
+  memset(buf, 0, kBufSize);
+}
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/system/gd/hal/hci_hal_host_test.cc b/system/gd/hal/hci_hal_impl_host_rootcanal_test.cc
similarity index 98%
rename from system/gd/hal/hci_hal_host_test.cc
rename to system/gd/hal/hci_hal_impl_host_rootcanal_test.cc
index d4539d6..5f67fe3 100644
--- a/system/gd/hal/hci_hal_host_test.cc
+++ b/system/gd/hal/hci_hal_impl_host_rootcanal_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "hal/hci_hal_host.h"
+#include "hal/hci_hal_impl_host.h"
 
 #include <bluetooth/log.h>
 #include <fcntl.h>
@@ -33,8 +33,9 @@
 #include <vector>
 
 #include "com_android_bluetooth_flags.h"
-#include "hal/hci_hal.h"
+#include "hal/hci_hal_host_rootcanal_config.h"
 #include "hal/serialize_packet.h"
+#include "module.h"  // ModuleRegistry
 #include "os/thread.h"
 #include "os/utils.h"
 #include "packet/raw_builder.h"
@@ -146,7 +147,8 @@
 
     HciHalHostRootcanalConfig::Get()->SetPort(kTestPort);
     fake_server_ = new FakeRootcanalDesktopHciServer;
-    hal_ = fake_registry_.Start<HciHal>(thread_, handler_);
+
+    hal_ = std::make_unique<HciHalImpl>();
     hal_->registerIncomingPacketCallback(&callbacks_);
     fake_server_socket_ =
             fake_server_->Accept();  // accept() after client is connected to avoid blocking
@@ -160,6 +162,7 @@
     if (com::android::bluetooth::flags::same_handler_for_all_modules()) {
       handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout);
     }
+    hal_.reset();
     fake_registry_.StopAll();
     delete handler_;
     close(fake_server_socket_);
@@ -174,7 +177,7 @@
   }
 
   FakeRootcanalDesktopHciServer* fake_server_ = nullptr;
-  HciHal* hal_ = nullptr;
+  std::unique_ptr<HciHal> hal_ = nullptr;
   ModuleRegistry fake_registry_;
   TestHciHalCallbacks callbacks_;
   int fake_server_socket_ = -1;
diff --git a/system/gd/hal/link_clocker.cc b/system/gd/hal/link_clocker.cc
index 0a7ee83..98f0b4f 100644
--- a/system/gd/hal/link_clocker.cc
+++ b/system/gd/hal/link_clocker.cc
@@ -102,6 +102,4 @@
   (*g_read_clock_handler).OnEvent(timestamp_us, bt_clock << 4);
 }
 
-const ModuleFactory LinkClocker::Factory = ModuleFactory([]() { return new LinkClocker(); });
-
 }  // namespace bluetooth::hal
diff --git a/system/gd/hal/link_clocker.h b/system/gd/hal/link_clocker.h
index 34286bf..30e023c 100644
--- a/system/gd/hal/link_clocker.h
+++ b/system/gd/hal/link_clocker.h
@@ -32,23 +32,12 @@
   virtual void OnEvent(uint32_t timestamp, uint32_t bt_clock) = 0;
 };
 
-class LinkClocker : public ::bluetooth::Module {
+class LinkClocker {
 public:
-  static const ModuleFactory Factory;
-
   void OnHciEvent(const HciPacket& packet);
 
   static void Register(ReadClockHandler*);
   static void Unregister();
-
-protected:
-  LinkClocker() = default;
-
-  void ListDependencies(ModuleList*) const override {}
-  void Start() override {}
-  void Stop() override {}
-
-  std::string ToString() const override { return std::string("LinkClocker"); }
 };
 
 }  // namespace bluetooth::hal
diff --git a/system/gd/hci/controller_impl_test.cc b/system/gd/hci/controller_impl_test.cc
index a05179e..3e10443 100644
--- a/system/gd/hci/controller_impl_test.cc
+++ b/system/gd/hci/controller_impl_test.cc
@@ -29,6 +29,7 @@
 #include "common/bind.h"
 #include "hci/address.h"
 #include "hci/hci_layer_fake.h"
+#include "module.h"  // FakeModuleRegistry
 #include "os/thread.h"
 #include "packet/raw_builder.h"
 
diff --git a/system/gd/hci/fuzz/fuzz_hci_layer.h b/system/gd/hci/fuzz/fuzz_hci_layer.h
index ee2db74..15e54d7 100644
--- a/system/gd/hci/fuzz/fuzz_hci_layer.h
+++ b/system/gd/hci/fuzz/fuzz_hci_layer.h
@@ -18,6 +18,7 @@
 
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <map>
 #include <vector>
 
 #include "fuzz/helpers.h"
diff --git a/system/gd/hci/fuzz/hci_layer_fuzz_test.cc b/system/gd/hci/fuzz/hci_layer_fuzz_test.cc
index a5dac3f..c55eb6a 100644
--- a/system/gd/hci/fuzz/hci_layer_fuzz_test.cc
+++ b/system/gd/hci/fuzz/hci_layer_fuzz_test.cc
@@ -38,9 +38,9 @@
   FuzzedDataProvider dataProvider(data, size);
 
   static FuzzTestModuleRegistry moduleRegistry = FuzzTestModuleRegistry();
-  FuzzHciHal* fuzzHal = moduleRegistry.Inject<FuzzHciHal>(&HciHal::Factory);
+  std::unique_ptr<FuzzHciHal> fuzzHal = std::make_unique<FuzzHciHal>();
   std::unique_ptr<HciInterface> hciLayer =
-          std::make_unique<HciLayer>(moduleRegistry.GetTestHandler(), fuzzHal);
+          std::make_unique<HciLayer>(moduleRegistry.GetTestHandler(), fuzzHal.get());
   std::unique_ptr<HciLayerFuzzClient> fuzzClient =
           std::make_unique<HciLayerFuzzClient>(moduleRegistry.GetTestHandler(), hciLayer.get());
 
@@ -58,6 +58,7 @@
 
   fuzzClient.reset();
   hciLayer.reset();
+  fuzzHal.reset();
   moduleRegistry.WaitForIdleAndStopAll();
   fake_timerfd_reset();
   return 0;
diff --git a/system/gd/hci/hci_layer_test.cc b/system/gd/hci/hci_layer_test.cc
index 81bde41..6346542 100644
--- a/system/gd/hci/hci_layer_test.cc
+++ b/system/gd/hci/hci_layer_test.cc
@@ -199,18 +199,15 @@
       counting_bytes.push_back(i);
       counting_down_bytes.push_back(~i);
     }
-    hal = new hal::TestHciHal();
-    fake_registry_.InjectTestModule(&hal::HciHal::Factory, hal);
-    fake_registry_.Start<hal::HciHal>(&fake_registry_.GetTestThread(),
-                                      fake_registry_.GetTestHandler());
 
+    hal = std::make_unique<hal::TestHciHal>();
     bluetooth::hci::testing::mock_storage_ = new storage::StorageModule(
             com::android::bluetooth::flags::same_handler_for_all_modules()
                     ? fake_registry_.GetTestHandler()
                     : new os::Handler(&fake_registry_.GetTestThread()));
     bluetooth::hci::testing::mock_storage_->Start();
 
-    hci = std::make_unique<HciLayer>(fake_registry_.GetTestHandler(), hal);
+    hci = std::make_unique<HciLayer>(fake_registry_.GetTestHandler(), hal.get());
     upper = std::make_unique<DependsOnHci>(fake_registry_.GetTestHandler(), hci.get());
 
     // Verify that reset was received
@@ -233,6 +230,7 @@
                                       std::chrono::milliseconds(20));
     upper.reset();
     hci.reset();
+    hal.reset();
     fake_registry_.StopAll();
   }
 
@@ -245,7 +243,7 @@
   }
 
   std::unique_ptr<DependsOnHci> upper = nullptr;
-  hal::TestHciHal* hal = nullptr;
+  std::unique_ptr<hal::TestHciHal> hal = nullptr;
   std::unique_ptr<HciLayer> hci = nullptr;
   TestModuleRegistry fake_registry_;
 };
diff --git a/system/gd/hci/hci_layer_unittest.cc b/system/gd/hci/hci_layer_unittest.cc
index 068fd95..8ab6854 100644
--- a/system/gd/hci/hci_layer_unittest.cc
+++ b/system/gd/hci/hci_layer_unittest.cc
@@ -85,12 +85,9 @@
 class HciLayerTest : public ::testing::Test {
 protected:
   void SetUp() override {
-    hal_ = new hal::TestHciHal();
-    fake_registry_.InjectTestModule(&hal::HciHal::Factory, hal_);
-    fake_registry_.Start<hal::HciHal>(&fake_registry_.GetTestThread(),
-                                      fake_registry_.GetTestHandler());
+    hal_ = std::make_unique<hal::TestHciHal>();
     hci_handler_ = fake_registry_.GetTestHandler();
-    hci_ = std::make_unique<HciLayer>(hci_handler_, hal_);
+    hci_ = std::make_unique<HciLayer>(hci_handler_, hal_.get());
     ::testing::FLAGS_gtest_death_test_style = "threadsafe";
     sync_handler();
   }
@@ -98,7 +95,7 @@
   void TearDown() override {
     fake_registry_.SynchronizeHandler(hci_handler_, std::chrono::milliseconds(20));
     hci_.reset();
-    fake_registry_.StopAll();
+    hal_.reset();
   }
 
   void FakeTimerAdvance(uint64_t ms) {
@@ -122,7 +119,7 @@
                      "assert failed: fake_registry_.GetTestThread().GetReactor()->WaitForIdle(2s)");
   }
 
-  hal::TestHciHal* hal_ = nullptr;
+  std::unique_ptr<hal::TestHciHal> hal_ = nullptr;
   std::unique_ptr<HciLayer> hci_ = nullptr;
   os::Handler* hci_handler_ = nullptr;
   TestModuleRegistry fake_registry_;
diff --git a/system/gd/hci/le_advertising_manager_impl_test.cc b/system/gd/hci/le_advertising_manager_impl_test.cc
index 38cbe15..21878eb 100644
--- a/system/gd/hci/le_advertising_manager_impl_test.cc
+++ b/system/gd/hci/le_advertising_manager_impl_test.cc
@@ -34,6 +34,7 @@
 #include "hci/hci_layer_fake.h"
 #include "hci/le_address_manager.h"
 #include "hci/le_on_advertising_set_terminated_interface.h"
+#include "module.h"
 #include "os/thread.h"
 #include "packet/raw_builder.h"
 
diff --git a/system/gd/lpp/lpp_offload_manager.cc b/system/gd/lpp/lpp_offload_manager.cc
index 4518cc9..1615fd1 100644
--- a/system/gd/lpp/lpp_offload_manager.cc
+++ b/system/gd/lpp/lpp_offload_manager.cc
@@ -26,24 +26,13 @@
 
 namespace bluetooth::lpp {
 
-const ModuleFactory LppOffloadManager::Factory =
-        ModuleFactory([]() { return new LppOffloadManager(); });
-
 struct LppOffloadManager::impl {
-  ~impl() {}
-
-  void start(os::Handler* handler, hal::SocketHal* socket_hal) {
+  impl(os::Handler* handler, hal::SocketHal* socket_hal)
+      : handler_(handler), socket_hal_(socket_hal) {
     log::info("");
-    handler_ = handler;
-    socket_hal_ = socket_hal;
     socket_capabilities_ = socket_hal_->GetSocketCapabilities();
   }
 
-  void stop() {
-    log::info("");
-    socket_capabilities_ = {};
-  }
-
   bool register_socket_hal_callbacks(hal::SocketHalCallback* callbacks) {
     log::info("");
     return socket_hal_->RegisterCallback(callbacks);
@@ -69,18 +58,12 @@
   hal::SocketCapabilities socket_capabilities_;
 };
 
-LppOffloadManager::LppOffloadManager() { pimpl_ = std::make_unique<impl>(); }
+LppOffloadManager::LppOffloadManager(os::Handler* handler, hal::SocketHal* socket_hal) {
+  pimpl_ = std::make_unique<impl>(handler, socket_hal);
+}
 
 LppOffloadManager::~LppOffloadManager() = default;
 
-void LppOffloadManager::ListDependencies(ModuleList* list) const { list->add<hal::SocketHal>(); }
-
-void LppOffloadManager::Start() { pimpl_->start(GetHandler(), GetDependency<hal::SocketHal>()); }
-
-void LppOffloadManager::Stop() { pimpl_->stop(); }
-
-std::string LppOffloadManager::ToString() const { return "Low Power Processor Offload Manager"; }
-
 bool LppOffloadManager::RegisterSocketHalCallback(hal::SocketHalCallback* callbacks) {
   return pimpl_->register_socket_hal_callbacks(callbacks);
 }
@@ -94,7 +77,7 @@
 }
 
 void LppOffloadManager::SocketClosed(uint64_t socket_id) {
-  CallOn(pimpl_.get(), &impl::socket_closed, socket_id);
+  pimpl_->handler_->CallOn(pimpl_.get(), &impl::socket_closed, socket_id);
 }
 
 }  // namespace bluetooth::lpp
diff --git a/system/gd/lpp/lpp_offload_manager.h b/system/gd/lpp/lpp_offload_manager.h
index c8b90a69..8a410e2 100644
--- a/system/gd/lpp/lpp_offload_manager.h
+++ b/system/gd/lpp/lpp_offload_manager.h
@@ -18,16 +18,15 @@
 #include <bluetooth/log.h>
 
 #include <memory>
-#include <string>
 
 #include "lpp_offload_interface.h"
-#include "module.h"
 
 namespace bluetooth::lpp {
 
-class LppOffloadManager : public bluetooth::Module, public LppOffloadInterface {
+/* Low Power Processor Offload Manager*/
+class LppOffloadManager : public LppOffloadInterface {
 public:
-  LppOffloadManager();
+  LppOffloadManager(os::Handler* handler, hal::SocketHal* socket_hal);
 
   LppOffloadManager(const LppOffloadManager&) = delete;
 
@@ -43,17 +42,6 @@
 
   void SocketClosed(uint64_t socket_id) override;
 
-  static const ModuleFactory Factory;
-
-protected:
-  void ListDependencies(ModuleList* list) const override;
-
-  void Start() override;
-
-  void Stop() override;
-
-  std::string ToString() const override;
-
 private:
   struct impl;
   std::unique_ptr<impl> pimpl_;
diff --git a/system/main/shim/entry.cc b/system/main/shim/entry.cc
index e85c031..93299af 100644
--- a/system/main/shim/entry.cc
+++ b/system/main/shim/entry.cc
@@ -57,7 +57,7 @@
 hal::SnoopLogger* GetSnoopLogger() { return Stack::GetInstance()->GetSnoopLogger(); }
 
 lpp::LppOffloadInterface* GetLppOffloadManager() {
-  return Stack::GetInstance()->GetInstance<lpp::LppOffloadManager>();
+  return Stack::GetInstance()->GetLppOffloadInterface();
 }
 
 storage::StorageModule* GetStorage() { return Stack::GetInstance()->GetStorage(); }
diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc
index e9173b1..32bacdb 100644
--- a/system/main/shim/stack.cc
+++ b/system/main/shim/stack.cc
@@ -29,7 +29,8 @@
 #include <string>
 
 #include "common/strings.h"
-#include "hal/hci_hal.h"
+#include "hal/hci_hal_impl.h"
+#include "hal/link_clocker.h"
 #include "hal/ranging_hal_impl.h"
 #include "hal/snoop_logger.h"
 #include "hci/acl_manager/acl_scheduler.h"
@@ -68,6 +69,9 @@
   Acl* acl_ = nullptr;
   std::shared_ptr<storage::StorageModule> storage_ = nullptr;
   std::shared_ptr<hal::SnoopLogger> snoop_logger_ = nullptr;
+  std::unique_ptr<lpp::LppOffloadManager> lpp_offload_manager_ = nullptr;
+  std::unique_ptr<hal::LinkClocker> link_clocker_ = nullptr;
+  std::unique_ptr<hal::HciHal> hci_hal_ = nullptr;
   std::unique_ptr<hal::RangingHal> ranging_hal_ = nullptr;
   std::unique_ptr<hci::HciLayer> hci_layer_ = nullptr;
   std::unique_ptr<hci::Controller> controller_ = nullptr;
@@ -108,11 +112,10 @@
 #if TARGET_FLOSS
     modules.add<sysprops::SyspropsModule>();
 #else
-    if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3286716
-      modules.add<lpp::LppOffloadManager>();
+    if (com::android::bluetooth::flags::socket_settings_api()) {
+      modules.add<hal::SocketHal>();
     }
 #endif
-    modules.add<hal::HciHal>();
 
     management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL);
     management_handler_ = new Handler(management_thread_);
@@ -142,8 +145,7 @@
     log::info("Successfully toggled Gd stack");
 
     // Make sure the leaf modules are started
-    log::assert_that(GetInstance<hal::HciHal>() != nullptr,
-                     "assert failed: GetInstance<hal::HciHal>() != nullptr");
+    log::assert_that(pimpl_->hci_hal_ != nullptr, "assert failed pimpl_->hci_hal_ != nullptr");
 
     pimpl_->acl_ = new Acl(stack_handler_, GetAclInterface());
 
@@ -227,6 +229,12 @@
   return pimpl_->snoop_logger_.get();
 }
 
+lpp::LppOffloadInterface* Stack::GetLppOffloadInterface() const {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  log::assert_that(is_running_, "assert failed: is_running_");
+  return pimpl_->lpp_offload_manager_.get();
+}
+
 hci::HciInterface* Stack::GetHciLayer() const {
   std::lock_guard<std::recursive_mutex> lock(mutex_);
   log::assert_that(is_running_, "assert failed: is_running_");
@@ -303,13 +311,27 @@
   pimpl_->snoop_logger_->Start();
   registry_.Start(modules, stack_thread_, stack_handler_);
 
-  auto hci_hal = static_cast<hal::HciHal*>(registry_.Get(&hal::HciHal::Factory));
+#ifndef TARGET_FLOSS
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3286716
+    auto socket_hal = static_cast<hal::SocketHal*>(registry_.Get(&hal::SocketHal::Factory));
+    log::info("Starting LppOffloadManager");
+    pimpl_->lpp_offload_manager_ =
+            std::make_unique<lpp::LppOffloadManager>(stack_handler_, socket_hal);
+  }
+#endif
+
+  log::info("Starting LinkClocker");
+  pimpl_->link_clocker_ = std::make_unique<hal::LinkClocker>();
+
+  log::info("Starting HciHal");
+  pimpl_->hci_hal_ = std::make_unique<hal::HciHalImpl>(stack_handler_, pimpl_->link_clocker_.get(),
+                                                       pimpl_->snoop_logger_.get());
 
   log::info("Starting RangingHal");
   pimpl_->ranging_hal_ = std::make_unique<hal::RangingHalImpl>();
 
   log::info("Starting HciLayer");
-  pimpl_->hci_layer_ = std::make_unique<hci::HciLayer>(stack_handler_, hci_hal);
+  pimpl_->hci_layer_ = std::make_unique<hci::HciLayer>(stack_handler_, pimpl_->hci_hal_.get());
 
   log::info("Starting Controller");
   pimpl_->controller_ =
@@ -329,7 +351,7 @@
 
   log::info("Starting MsftExtensionManager");
   pimpl_->msft_extension_manager_ = std::make_unique<hci::MsftExtensionManager>(
-          stack_handler_, hci_hal, pimpl_->hci_layer_.get());
+          stack_handler_, pimpl_->hci_hal_.get(), pimpl_->hci_layer_.get());
 
   log::info("Starting LeScanningManagerImpl");
   pimpl_->le_scanning_manager_ = std::make_unique<hci::LeScanningManagerImpl>(
@@ -380,6 +402,17 @@
   log::info("Stopping RangingHal");
   pimpl_->ranging_hal_.reset();
 
+  log::info("Stopping HciHal");
+  pimpl_->hci_hal_.reset();
+
+  log::info("Stopping LinkClocker");
+  pimpl_->link_clocker_.reset();
+
+  if (pimpl_->lpp_offload_manager_) {
+    log::info("Stopping LppOffloadManager");
+    pimpl_->lpp_offload_manager_.reset();
+  }
+
   registry_.StopAll();
 
   pimpl_->snoop_logger_->Stop();
diff --git a/system/main/shim/stack.h b/system/main/shim/stack.h
index 492e991..7721e40 100644
--- a/system/main/shim/stack.h
+++ b/system/main/shim/stack.h
@@ -25,6 +25,7 @@
 #include "hci/le_advertising_manager.h"
 #include "hci/le_scanning_manager.h"
 #include "hci/remote_name_request.h"
+#include "lpp/lpp_offload_interface.h"
 #include "module.h"
 #include "os/handler.h"
 #include "os/thread.h"
@@ -78,6 +79,7 @@
   virtual Acl* GetAcl() const;
   virtual storage::StorageModule* GetStorage() const;
   virtual hal::SnoopLogger* GetSnoopLogger() const;
+  virtual lpp::LppOffloadInterface* GetLppOffloadInterface() const;
   virtual hci::HciInterface* GetHciLayer() const;
   virtual hci::Controller* GetController() const;
   virtual hci::RemoteNameRequestModule* GetRemoteNameRequest() const;
diff --git a/system/main/test/main_shim_test.cc b/system/main/test/main_shim_test.cc
index b604c49..29d7584 100644
--- a/system/main/test/main_shim_test.cc
+++ b/system/main/test/main_shim_test.cc
@@ -311,10 +311,6 @@
 }  // namespace testing
 }  // namespace shim
 
-namespace hal {
-const ModuleFactory HciHal::Factory = ModuleFactory([]() { return nullptr; });
-}  // namespace hal
-
 }  // namespace bluetooth
 
 class MainShimTest : public testing::Test {
diff --git a/system/osi/src/alarm.cc b/system/osi/src/alarm.cc
index 6aaf435..f12c287 100644
--- a/system/osi/src/alarm.cc
+++ b/system/osi/src/alarm.cc
@@ -23,6 +23,7 @@
 #include <android_bluetooth_sysprop.h>
 #include <base/cancelable_callback.h>
 #include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
 #include <fcntl.h>
 #include <hardware/bluetooth.h>
 #include <malloc.h>
@@ -244,6 +245,9 @@
   {
     std::lock_guard<std::mutex> lock(alarms_mutex);
     local_mutex_ref = alarm->callback_mutex;
+    if (com::android::bluetooth::flags::set_ptr_null_after_free()) {
+      log::assert_that(local_mutex_ref != nullptr, "assert failed: local_mutex_ref != nullptr");
+    }
     alarm_cancel_internal(alarm);
   }
 
diff --git a/system/osi/test/fuzzers/alarm/Android.bp b/system/osi/test/fuzzers/alarm/Android.bp
index 3af1096..88d93f2 100644
--- a/system/osi/test/fuzzers/alarm/Android.bp
+++ b/system/osi/test/fuzzers/alarm/Android.bp
@@ -22,6 +22,8 @@
         "libstatssocket",
     ],
     static_libs: [
+        "bluetooth_flags_c_lib",
+        "libaconfig_storage_read_api_cc",
         "libbluetooth_log",
         "libbt-common",
         "libchrome",
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 2538e09..fd303a2 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -324,8 +324,15 @@
     },
 }
 
-cc_fuzz {
-    name: "sdp-fuzzer",
+filegroup {
+    name: "SdpFuzzerSources",
+    srcs: [
+        "fuzzers/sdp_fuzzer.cc",
+    ],
+}
+
+cc_defaults {
+    name: "sdp_fuzzer_default",
     defaults: [
         "btstack_fuzzer_default",
         "fluoride_defaults",
@@ -335,6 +342,7 @@
     ],
     srcs: [
         ":LegacyStackSdp",
+        ":SdpFuzzerSources",
         ":TestCommonMockFunctions",
         ":TestFakeOsi",
         ":TestMockBtif",
@@ -342,7 +350,6 @@
         ":TestMockMainShim",
         ":TestMockStackBtm",
         ":TestMockStackL2cap",
-        "fuzzers/sdp_fuzzer.cc",
     ],
     static_libs: [
         "bluetooth_flags_c_lib",
@@ -357,6 +364,14 @@
     ],
 }
 
+cc_fuzz {
+    name: "sdp-fuzzer",
+    team: "trendy_team_bluetooth",
+    defaults: [
+        "sdp_fuzzer_default",
+    ],
+}
+
 filegroup {
     name: "RfcommFuzzerSources",
     srcs: [
@@ -424,8 +439,18 @@
     ],
 }
 
-cc_fuzz {
-    name: "gatt-fuzzer",
+filegroup {
+    name: "GattFuzzerSources",
+    srcs: [
+        "ais/*.cc",
+        "eatt/*.cc",
+        "fuzzers/gatt_fuzzer.cc",
+        "gatt/*.cc",
+    ],
+}
+
+cc_defaults {
+    name: "gatt_fuzzer_default",
     defaults: [
         "btstack_fuzzer_default",
         "fluoride_defaults",
@@ -433,10 +458,12 @@
     include_dirs: [
         "external/flatbuffers/include",
         "external/rust/android-crates-io/crates/quiche/deps/boringssl/src/include",
+        "packages/modules/Bluetooth/system/stack",
         "packages/modules/Bluetooth/system/stack/btm",
     ],
     srcs: [
         ":BluetoothPacketSources",
+        ":GattFuzzerSources",
         ":TestCommonMockFunctions",
         ":TestCommonStackConfig",
         ":TestFakeOsi",
@@ -453,10 +480,6 @@
         ":TestMockStackHcic",
         ":TestMockStackL2cap",
         ":TestMockStackSdp",
-        "ais/*.cc",
-        "eatt/*.cc",
-        "fuzzers/gatt_fuzzer.cc",
-        "gatt/*.cc",
     ],
     shared_libs: [
         "libaconfig_storage_read_api_cc",
@@ -478,6 +501,14 @@
 }
 
 cc_fuzz {
+    name: "gatt-fuzzer",
+    team: "trendy_team_bluetooth",
+    defaults: [
+        "gatt_fuzzer_default",
+    ],
+}
+
+cc_fuzz {
     name: "smp-fuzzer",
     defaults: [
         "btstack_fuzzer_default",
diff --git a/system/stack/fuzzers/test/Android.bp b/system/stack/fuzzers/test/Android.bp
index 45540e6..26bbbe4 100644
--- a/system/stack/fuzzers/test/Android.bp
+++ b/system/stack/fuzzers/test/Android.bp
@@ -27,13 +27,12 @@
         android: {
             sanitize: {
                 hwaddress: true,
-                all_undefined: true,
             },
             test_config_template: "FuzzerValidationTestTemplate.xml",
         },
         host: {
             sanitize: {
-                all_undefined: true,
+                address: true,
             },
             test_config_template: "FuzzerValidationHostTestTemplate.xml",
         },
@@ -45,6 +44,16 @@
     srcs: ["data/rfcomm_corpus.zip"],
 }
 
+filegroup {
+    name: "sdp-fuzzer-corpus",
+    srcs: ["data/sdp_corpus.zip"],
+}
+
+filegroup {
+    name: "gatt-fuzzer-corpus",
+    srcs: ["data/gatt_corpus.zip"],
+}
+
 cc_test {
     name: "rfcomm-fuzzer-validation-test",
     defaults: [
@@ -56,3 +65,27 @@
         "rfcomm_fuzzer_validation_test.cc",
     ],
 }
+
+cc_test {
+    name: "sdp-fuzzer-validation-test",
+    defaults: [
+        "fuzzer_validation_test_default",
+        "sdp_fuzzer_default",
+    ],
+    data: [":sdp-fuzzer-corpus"],
+    srcs: [
+        "sdp_fuzzer_validation_test.cc",
+    ],
+}
+
+cc_test {
+    name: "gatt-fuzzer-validation-test",
+    defaults: [
+        "fuzzer_validation_test_default",
+        "gatt_fuzzer_default",
+    ],
+    data: [":gatt-fuzzer-corpus"],
+    srcs: [
+        "gatt_fuzzer_validation_test.cc",
+    ],
+}
diff --git a/system/stack/fuzzers/test/data/gatt_corpus.zip b/system/stack/fuzzers/test/data/gatt_corpus.zip
new file mode 100644
index 0000000..322626f
--- /dev/null
+++ b/system/stack/fuzzers/test/data/gatt_corpus.zip
Binary files differ
diff --git a/system/stack/fuzzers/test/data/sdp_corpus.zip b/system/stack/fuzzers/test/data/sdp_corpus.zip
new file mode 100644
index 0000000..6267d74
--- /dev/null
+++ b/system/stack/fuzzers/test/data/sdp_corpus.zip
Binary files differ
diff --git a/system/stack/fuzzers/test/fuzzer_validation_test.cc b/system/stack/fuzzers/test/fuzzer_validation_test.cc
index 621498b..4687aaf 100644
--- a/system/stack/fuzzers/test/fuzzer_validation_test.cc
+++ b/system/stack/fuzzers/test/fuzzer_validation_test.cc
@@ -38,6 +38,11 @@
 // Allow referencing of fuzzer entrance function as-is.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
 
+// TODO: b/280300628 for fixing memory leaks. Until it's fixed leak
+// detection needs to be turned off to unblock fuzzing.
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+extern "C" const char *__asan_default_options() { return "detect_leaks=0"; }
+
 void runFuzzerOnCorpusAndExitProcess() {
   for (const auto &corpus_entry : fs::directory_iterator("./data/corpus")) {
     if (!corpus_entry.is_regular_file()) {
@@ -52,6 +57,7 @@
     corpus.read(data, size);
     corpus.close();
     LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(data), size);
+    delete[] data;
   }
   exit(0);
 }
diff --git a/system/stack/fuzzers/test/gatt_fuzzer_validation_test.cc b/system/stack/fuzzers/test/gatt_fuzzer_validation_test.cc
new file mode 100644
index 0000000..5d58e82
--- /dev/null
+++ b/system/stack/fuzzers/test/gatt_fuzzer_validation_test.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2025 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 "fuzzer_validation_test.h"
+
+using fuzzer_validation_test::runValidationTest;
+using fuzzer_validation_test::TestEnvironment;
+using testing::InitGoogleTest;
+
+// Write own main function to allow for plugging in the test environment, which runs only once.
+int main(int argc, char** argv) {
+  InitGoogleTest(&argc, argv);
+  TestEnvironment* env = new TestEnvironment("gatt_corpus");
+  AddGlobalTestEnvironment(env);
+  return RUN_ALL_TESTS();
+}
+
+TEST(GattFuzzerValidationTest, DoesNotCrashOnCorpus) { runValidationTest(); }
diff --git a/system/stack/fuzzers/test/sdp_fuzzer_validation_test.cc b/system/stack/fuzzers/test/sdp_fuzzer_validation_test.cc
new file mode 100644
index 0000000..99f7c43
--- /dev/null
+++ b/system/stack/fuzzers/test/sdp_fuzzer_validation_test.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2025 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 "fuzzer_validation_test.h"
+
+using fuzzer_validation_test::runValidationTest;
+using fuzzer_validation_test::TestEnvironment;
+using testing::InitGoogleTest;
+
+// Write own main function to allow for plugging in the test environment, which runs only once.
+int main(int argc, char** argv) {
+  InitGoogleTest(&argc, argv);
+  TestEnvironment* env = new TestEnvironment("sdp_corpus");
+  AddGlobalTestEnvironment(env);
+  return RUN_ALL_TESTS();
+}
+
+TEST(SdpFuzzerValidationTest, DoesNotCrashOnCorpus) { runValidationTest(); }