Android Bluetooth is easier to debug once you stop treating it as one service. A scan request crosses process, language, protocol, vendor, and hardware boundaries; a failure at each boundary leaves different evidence. This guide gives those boundaries names and shows where to look.

The short architectural answer

An app uses android.bluetooth APIs. Binder carries privileged work into Android’s Bluetooth process/module. Java services and profile implementations cross JNI into the native host stack, which implements Bluetooth protocols and talks through the device’s vendor HAL/transport to the controller using HCI commands, events, and data.

Current AOSP layer map

android-bluetooth-path.txttext
App / system component
  android.bluetooth APIs
          │ Binder IPC + permission checks
Bluetooth framework/process/module
  AdapterService + profile services
          │ JNI callbacks and native calls
Native Bluetooth host stack
  GAP/GATT, SDP, L2CAP, RFCOMM, profiles, security
          │ Bluetooth HAL / vendor boundary
Vendor transport integration
  UART / USB / shared transport, firmware, power control
          │ HCI commands, events, ACL/SCO/ISO data
Bluetooth controller + radio

Conceptual call path; exact processes and interface versions depend on the Android branch and product.

How to read this map

  • Binder is the application/framework process boundary; JNI is the Java/native boundary. They are not interchangeable terms.

  • The host stack runs on the application processor; the controller executes link-layer/radio work and reports events over HCI.

  • A public API returning an error does not prove the radio failed—the request may have been rejected by permissions, state, policy, service lifecycle, or a profile.

  • Vendor integration and HAL transport differ by release and product. Inspect source, VINTF manifest, running services, and logs on the exact build.

1. App-facing framework APIs

Applications normally enter through classes such as BluetoothManager, BluetoothAdapter, BluetoothDevice, BluetoothGatt, and profile proxies. These objects do not drive UART or issue HCI commands directly. They validate state and arguments, enforce SDK-visible behavior, and invoke system-owned interfaces.

  • Classic Bluetooth: discovery, bonding, sockets, and profiles such as A2DP, HFP, HID, and PAN.

  • Bluetooth Low Energy: scanning, advertising, GATT client/server operations, PHY/data-length features, and newer LE Audio capabilities when supported.

  • Permissions: behavior is release- and target-SDK-sensitive. Modern Android uses nearby-device permissions such as BLUETOOTH_SCAN and BLUETOOTH_CONNECT; older releases tied some scans to location permissions.

  • Asynchrony: most meaningful results arrive through callbacks, broadcasts, Binder callbacks, or profile state changes—not from the initiating method alone.

2. Binder and the Bluetooth process

Binder separates app callers from privileged Bluetooth implementation code. The Bluetooth side owns adapter state, bonding, discovery, profile services, database/state coordination, and permission/policy enforcement. AdapterService remains a useful orientation point in current AOSP, but it is not the whole stack.

request-and-callback-flow.txttext
scan request
  app → API proxy → Binder → Bluetooth service
      → JNI → native scanning/GAP logic → HAL/HCI command
 
controller result
  HCI event → vendor/HAL → native stack → JNI callback
      → service filtering/state → Binder callback → app

A successful operation crosses down the stack and reports state back asynchronously.

What the return path teaches

  • The callback path is as important as the request path; missing results can be caused by filtering, permissions, process death, or callback registration even when HCI traffic exists.

  • Binder transaction failures point above the native protocol stack; HCI command timeout/reset evidence points near the controller path.

  • Framework-visible device data may be transformed, cached, redacted, or permission-filtered before reaching the app.

  • Correlate one user action with timestamps across app logs, Bluetooth service dumps, system logs, and—when authorized—HCI capture.

3. JNI and the native host stack

JNI code under the Bluetooth module connects Java service objects and callbacks to native interfaces. Below it, the AOSP host stack handles protocol state machines, security, discovery, connections, GATT, L2CAP, SDP, RFCOMM, and profiles. Fluoride is commonly used as the modern name; GD refers to newer modularized components within its evolution, not a second physical controller.

  • JNI bugs often look like lost callbacks, stale native handles, lifecycle races, conversion errors, or exceptions around service restart.

  • Native stack bugs often expose state-machine transitions, protocol status codes, queue congestion, timeouts, or peer interoperability patterns.

  • Profile behavior can involve audio, telephony, media, networking, or companion services outside the Bluetooth module boundary.

  • Never assume a source filename observed on main exists unchanged on a vendor branch; search the checked-out tree and record its build fingerprint/tag.

4. HAL, vendor implementation and HCI

The vendor boundary connects the AOSP host stack to chipset-specific implementation, firmware loading, power sequencing, and transport. Current AOSP Bluetooth documentation still describes the Bluetooth vendor interface using HIDL, even though Android’s general HAL direction favors stable AIDL and HIDL is deprecated for new HAL design. The device manifest and branch are authoritative.

host-controller-boundary.txttext
Android host stack
   ├─ Android HAL/interface lifecycle, errors, callbacks
Vendor implementation / transport driver
   ├─ HCI command packets  ───────────────► controller
   ├─ HCI event packets    ◄─────────────── controller
   └─ ACL / SCO / ISO data ◄──────────────► controller

HCI is a protocol boundary; HAL is an Android software integration boundary.

Keep these boundaries separate

  • A HAL service can be registered while the controller transport is broken. Service presence is necessary evidence, not proof of radio health.

  • HCI snoop logs show host/controller protocol traffic, not over-the-air packets and not every vendor-internal diagnostic.

  • UART framing, USB enumeration, shared-transport arbitration, firmware download, GPIO/regulator sequencing, and sleep/wake faults sit below normal profile logic.

  • Vendor-specific commands and events require chipset documentation; do not guess opcodes or replay captures on production hardware.

Android release map: what moved

  • Early Android / BlueDroid era: documentation referenced packages/apps/Bluetooth, external/bluetooth/bluedroid, legacy hardware/libhardware headers, and libbt-vendor. Preserve these paths only for matching historical branches.

  • Modern source tree: AOSP places the app/JNI code under packages/modules/Bluetooth/android/app and the native stack under packages/modules/Bluetooth/system.

  • Android 13: Bluetooth was introduced as an optional Mainline module using an APEX package boundary.

  • Android 16 and later: AOSP documentation describes the module as updatable and containing a certified dual-mode host stack. Product packaging can still differ, so verify the installed module rather than inferring from OS version alone.

Find the implementation in an AOSP checkout

$ANDROID_BUILD_TOPbash
rg -n 'class AdapterService|class BluetoothManagerService' packages/modules/Bluetooth frameworks/base
rg -n 'android.hardware.bluetooth|IBluetoothHci' hardware/interfaces packages/modules/Bluetooth
rg -n 'libbluetooth_jni|bluetooth.default' packages/modules/Bluetooth hardware

Why source search beats memorized paths

  • rg -n reports the file and line containing each symbol; results describe the checked-out branch, not a generic online branch.

  • BluetoothManagerService and module services live on different sides of framework/module boundaries, so search both roots.

  • HAL interface names and build targets reveal what the branch supports, but product manifests and runtime service registration decide what the device uses.

  • Generated files and vendor repositories may be outside these roots. Extend the search deliberately rather than assuming no implementation exists.

Inspect a running Android device

Terminaladb
adb shell getprop ro.build.fingerprint
adb shell dumpsys bluetooth_manager
adb shell service list | grep -i bluetooth
adb shell dumpsys package com.android.bluetooth

Read the evidence in order

  • Record the build fingerprint first; Bluetooth behavior and source paths are meaningless without a release/product identity.

  • dumpsys bluetooth_manager is privileged diagnostic state whose fields vary by release. Capture it before and after one reproduced action.

  • The Binder service list confirms registered framework services, not successful HAL/controller operation.

  • Package output can reveal APK/APEX placement and versioning, but package names differ on AOSP, GMS, and vendor products.

  • Diagnostics may contain device names, addresses, account/package data, and connection history. Redact before sharing.

Correlate logs without drowning in noise

Terminaladb
adb logcat -c
adb logcat -v threadtime | grep -Ei 'Bluetooth|bt_stack|btif|hci|AdapterService'

A disciplined trace is more useful than a huge log

  • Clearing logcat discards the current volatile buffer; use it only on an authorized test device after preserving evidence you need.

  • Start capture immediately before one action, note the exact time, stop after the failure, and keep an unfiltered copy when possible.

  • Tag names change across releases and vendor builds; broad filtering is discovery, not a permanent diagnostic recipe.

  • Never publish raw logs containing Bluetooth addresses, names, serials, phone data, or application identifiers.

Symptom-to-layer debugging

  • SecurityException before scanning: begin with app permissions, attribution, target SDK, foreground/background rules, and policy—not the controller.

  • Bluetooth service absent or repeatedly restarts: inspect Binder/service lifecycle, crashes, tombstones, module/package state, SELinux denials, and dependencies.

  • Native enable timeout or controller reset: inspect HAL service, transport, firmware, power sequencing, HCI timeouts, and vendor logs.

  • Discovery runs but one peer is missing: separate Classic inquiry from LE scan; check filters, PHY/advertising mode, privacy/address rotation, controller capabilities, and peer behavior.

  • Pairs but profile fails: pairing/security success does not prove A2DP, HFP, GATT, or another profile is enabled, compatible, connected, and routed.

  • HCI shows events but app sees nothing: follow JNI callbacks, service filtering/cache, Binder callbacks, permissions, app lifecycle, and scan/profile filters.

A repeatable investigation workflow

  1. Identify device, build fingerprint, Android version, module/package version, chipset, transport, and exact peer.

  2. Define one failing operation and its expected observable result.

  3. Locate the corresponding public/System API and Binder-facing service on the matching source branch.

  4. Capture framework/service state and timestamped logs around one reproduction.

  5. Decide whether evidence crosses Binder, JNI, native stack, HAL, and HCI boundaries.

  6. Change one variable—peer, app, profile, cable/transport condition, build, or controller firmware—and compare.

  7. Confirm the fix at the user-visible layer and ensure it survives Bluetooth toggle, process restart, reboot, suspend, and reconnect as relevant.

Before calling the issue fixed

  • Retest the exact peer and operation that originally failed, then test one known-good peer to separate interoperability from regression.

  • Verify adapter toggle, process restart, reboot, suspend/resume, reconnect, and airplane-mode transitions relevant to the product.

  • Confirm that logs are free of new crashes, repeated resets, permission denials, and silent fallback—not merely that the UI appears connected.

  • Document the build, module version, controller firmware, reproduction, evidence boundary, code change, and regression test so the diagnosis survives the next rebase.

Primary references