When a user toggles the Bluetooth switch in Android Settings or an application calls BluetoothAdapter.enable(), the Android Open Source Project (AOSP) triggers a multi-layered initialization sequence across Binder IPC, Java System Services, JNI C++ bindings, Fluoride native stack, and Hardware Abstraction Layer (HAL) daemons.

Beyond powering on the Bluetooth radio controller, Android must dynamically load and activate individual Bluetooth Profile Services—such as Advanced Audio Distribution Profile (A2DP), Hands-Free Profile (HFP), Generic Attribute Profile (GATT), and Human Interface Device (HID). In this article, we trace the full architectural flow of Bluetooth profile enablement in AOSP.

AOSP Bluetooth Stack Layered Architecture

  • Application & Framework Layer (`android.bluetooth`): Exposes public APIs like BluetoothAdapter, BluetoothA2dp, and BluetoothGatt to app developers via IPC proxies.

  • System Service Layer (`com.android.bluetooth`): Runs inside a dedicated APK process hosting AdapterService and individual profile services (A2dpService, HeadsetService, GattService).

  • JNI Layer (`com_android_bluetooth_*.cpp`): Bridges Java AdapterService calls to C++ native interfaces.

  • Native Stack Layer (Fluoride / BlueDroid): Handles L2CAP, RFCOMM, SDP, and BTA/BTE protocol state machines.

  • HAL Daemon (`android.hardware.bluetooth@1.1-service`): Communicates with the physical Bluetooth chip vendor driver over UART or USB via V4L2/HCI commands.

1. Initialization: SystemServer to AdapterService

During device boot, SystemServer starts BluetoothManagerService. When Bluetooth activation is requested, BluetoothManagerService binds to the BluetoothManagerApp (com.android.bluetooth) to start AdapterService:

AdapterService.java (AOSP Source Trace)java
// AOSP: packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java
public class AdapterService extends Service {
    private AdapterState mAdapterStateMachine;
    private ProfileService[] mProfiles;
 
    public void processAdapterStateChange(int newState) {
        if (newState == BluetoothAdapter.STATE_TURNING_ON) {
            // Enable native Fluoride stack via JNI
            enableNative();
        } else if (newState == BluetoothAdapter.STATE_ON) {
            // Initialize and start enabled profile services
            startProfileServices();
        }
    }
 
    private void startProfileServices() {
        Class[] supportedProfiles = Config.getSupportedProfiles();
        for (Class profileClass : supportedProfiles) {
            setProfileAutoConnection(profileClass);
            startProfile(profileClass);
        }
    }
}

2. Profile Activation and AOSP Resource Overrides

Which Bluetooth profiles are enabled on a specific Android device is determined at build time by resource boolean flags defined in AOSP overlay files (frameworks/base/core/res/res/values/config.xml):

config.xml (AOSP Device Overlay Configuration)xml
<!-- Enable A2DP (Bluetooth Audio Sink/Source) profile -->
<bool name="config_bluetooth_supports_a2dp">true</bool>
 
<!-- Enable HFP (Hands-Free Telephony) profile -->
<bool name="config_bluetooth_supports_hfp">true</bool>
 
<!-- Enable HID Host (Keyboard/Mouse) profile -->
<bool name="config_bluetooth_supports_hid">true</bool>
 
<!-- Enable PAN (Personal Area Network) profile -->
<bool name="config_bluetooth_supports_pan">false</bool>

3. Connecting Service Bindings to Client Apps

When a third-party application requests interaction with a Bluetooth profile (e.g. streaming audio or scanning BLE sensors), it obtains a profile proxy object using getProfileProxy():

ClientApp.javajava
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 
bluetoothAdapter.getProfileProxy(context, new BluetoothProfile.ServiceListener() {
    @Override
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        if (profile == BluetoothProfile.A2DP) {
            BluetoothA2dp a2dpService = (BluetoothA2dp) proxy;
            List<BluetoothDevice> connectedDevices = a2dpService.getConnectedDevices();
            Log.d("BluetoothProfile", "Connected A2DP devices: " + connectedDevices.size());
        }
    }
 
    @Override
    public void onServiceDisconnected(int profile) {
        Log.d("BluetoothProfile", "Profile service disconnected");
    }
}, BluetoothProfile.A2DP);

Debugging Bluetooth Profiles via ADB Shell

  • Inspect Active Profile Service States: Execute adb shell dumpsys bluetooth_manager to view registered profile services, connected MAC addresses, and state machine histories.

  • Capture HCI Packet Logs (snoop log): Enable Enable Bluetooth HCI snoop log in Android Developer Options, then pull raw packet logs from /data/misc/bluetooth/logs/btsnoop_hci.log for Wireshark analysis.

  • Filter Bluetooth Logcat Logs: Filter native Fluoride and service logs using adb logcat -s BluetoothAdapterService:V BluetoothA2dpService:V bt_btif:V.