【android bluetooth 框架分析 04】【bt-framework 层详解 8】【DeviceProperties介绍】

发布于:2025-06-24 ⋅ 阅读:(14) ⋅ 点赞:(0)

前面我们提到了 蓝牙协议栈中的 Properties , 这篇文章是 他的补充。

1. 设计初衷与核心问题

1. 为什么要设计 DeviceProperties

在 Android 蓝牙实际使用中,系统需反复处理设备的发现、服务解析、配对、连接等场景。在这些过程中,远程设备的信息管理混乱、数据缺失、不一致是普遍存在的问题。

2. DeviceProperties 解决的问题:

场景 待解决问题 设计目标
搜索 同一设备多次出现在列表中,名称等信息丢失 唯一标识设备、统一管理搜索信息
SDP 每次连接都重新做服务发现,耗时、重复 缓存 UUID,提高连接效率
配对 配对状态混乱、无法判断安全能力 缓存密钥与能力,便于复用
连接 无法快速判断设备是否支持某 profile 统一缓存 profile 能力与状态

因此,AOSP 中通过 DeviceProperties 实现了一个 以 MAC 地址为主键的远程设备状态缓存中心,并与 StorageModule 联动实现持久化。


2. DeviceProperties 模块设计概述

1. 核心职责

功能 说明
缓存设备属性 设备名称、类型、Class of Device、UUID、RSSI、Bond 状态、安全能力、Link Key 等
提供统一读写接口 供 btif 层、profile 层、JNI 层查询与设置设备状态
StorageModule 协作持久化 关键属性写入 bt_config.conf 配置文件,保证系统重启后信息不丢失

2. 数据存储结构

每个远程设备(用 address 唯一标识)对应一个 DeviceProperties 实例,核心字段如:

android/app/src/com/android/bluetooth/btservice/RemoteDevices.java

    class DeviceProperties {
        private String mName;
        private byte[] mAddress;
        private String mIdentityAddress;
        private boolean mIsConsolidated = false;
        private int mBluetoothClass = BluetoothClass.Device.Major.UNCATEGORIZED;
        private int mBredrConnectionHandle = BluetoothDevice.ERROR;
        private int mLeConnectionHandle = BluetoothDevice.ERROR;
        private short mRssi;
        private String mAlias;
        private BluetoothDevice mDevice;
        private boolean mIsBondingInitiatedLocally;
        private int mBatteryLevelFromHfp = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
        private int mBatteryLevelFromBatteryService = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
        private boolean mIsCoordinatedSetMember;
        private int mAshaCapability;
        private int mAshaTruncatedHiSyncId;
        private String mModelName;
        @VisibleForTesting int mBondState;
        @VisibleForTesting int mDeviceType;
        @VisibleForTesting ParcelUuid[] mUuids;
        private BluetoothSinkAudioPolicy mAudioPolicy;

		...
}

1. 创建 DeviceProperties 对象

创建 DeviceProperties 的地方:

android/app/src/com/android/bluetooth/btservice/RemoteDevices.java

    DeviceProperties addDeviceProperties(byte[] address) {
        synchronized (mDevices) {
            DeviceProperties prop = new DeviceProperties();  // 1. 创建 DeviceProperties 对象
            prop.setDevice(sAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)));
            prop.setAddress(address);
            String key = Utils.getAddressStringFromByte(address); // 2.key 是 mac 地址
            DeviceProperties pv = mDevices.put(key, prop); // 保存在 RemoteDevices.mDevices 中
			...
            return prop;
        }
    }

java 层在如下场景中,会调用 addDeviceProperties 创建一个 DeviceProperties 对象:

  1. AdapterProperties.adapterPropertyChangedCallback:BT_PROPERTY_ADAPTER_BONDED_DEVICES
    • 在打开蓝牙时, AdapterProperties 会收到 BT_PROPERTY_ADAPTER_BONDED_DEVICES 事件;此时会将之前 已经配对的设备 封装为一个个 DeviceProperties 对象。
  2. BondStateMachine.sspRequestCallback
    • 设备配对时支持 SSP 模式,进行确认、比较、输入密钥等操作时触发
  3. BondStateMachine.pinRequestCallback
    • 当连接传统蓝牙设备(BR/EDR)时需要输入 PIN 码进行配对时触发
  4. RemoteDevices.devicePropertyChangedCallback
    • 当 设备 属性发生变化时, 从 native -> java 上报设备信息时,如果找不到对应设备的 Property 将新建一个。

2. 管理那些属性:

【android bluetooth 框架分析 04】【bt-framework 层详解 6】【Properties介绍】 中有详细介绍。

枚举常量 说明 使用范围 数据类型 访问权限
🔁 适用于 Adapter 和 Remote Device
BT_PROPERTY_BDNAME 设备名称 Adapter: 读/写Remote Device: 只读 bt_bdname_t GET / SET(Adapter)GET(Remote)
BT_PROPERTY_BDADDR 设备地址 Adapter & Remote Device RawAddress GET
BT_PROPERTY_UUIDS 支持的服务 UUID 列表 Remote Device bluetooth::Uuid[] GET
BT_PROPERTY_CLASS_OF_DEVICE 类别码 Remote Device uint32_t GET
BT_PROPERTY_TYPE_OF_DEVICE 设备类型(BR/EDR/LE) Remote Device bt_device_type_t GET
BT_PROPERTY_SERVICE_RECORD 服务记录 Remote Device bt_service_record_t GET
枚举常量 说明 使用范围 数据类型 访问权限
📡 仅适用于 Remote Device(远程设备)
BT_PROPERTY_REMOTE_FRIENDLY_NAME 远程设备名称(用户设定) Remote Device bt_bdname_t GET / SET
BT_PROPERTY_REMOTE_RSSI 远程设备 RSSI Remote Device int8_t GET
BT_PROPERTY_REMOTE_VERSION_INFO 远程设备协议版本信息 Remote Device bt_remote_version_t GET / SET
BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER 是否是协同设备成员 Remote Device bool GET
BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP 属性刷新时间戳 Remote Device int64_t(或自定义) GET

3. native -> java callback

在 搜索、 配对 、 sdp 的过程中,native 在不同阶段都会触发 回调 到 java 层,来更新 DeviceProperties .

协议栈会通过 下面两个函数来, 层层 上报 属性到 java 层:

  1. invoke_device_found_cb
  2. invoke_remote_device_properties_cb

接下来我们梳理一下 他们的调用逻辑。

1. invoke_device_found_cb & invoke_remote_device_properties_cb
// system/btif/src/bluetooth.cc

void invoke_device_found_cb(int num_properties, bt_property_t* properties) {
  do_in_jni_thread(FROM_HERE,
                   base::BindOnce(
                       [](int num_properties, bt_property_t* properties) {
                         HAL_CBACK(bt_hal_cbacks, device_found_cb,
                                   num_properties, properties); // 调用 jni 函数
                         if (properties) {
                           osi_free(properties);
                         }
                       },
                       num_properties,
                       property_deep_copy_array(num_properties, properties)));
}

void invoke_remote_device_properties_cb(bt_status_t status, RawAddress bd_addr,
                                        int num_properties,
                                        bt_property_t* properties) {
  do_in_jni_thread(
      FROM_HERE, base::BindOnce(
                     [](bt_status_t status, RawAddress bd_addr,
                        int num_properties, bt_property_t* properties) {
                       HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
                                 status, &bd_addr, num_properties, properties);  // 调用 jni 函数
                       if (properties) {
                         osi_free(properties);
                       }
                     },
                     status, bd_addr, num_properties,
                     property_deep_copy_array(num_properties, properties)));
}

// android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp

typedef struct {
  ...
  remote_device_properties_callback remote_device_properties_cb;
  device_found_callback device_found_cb;
  ...
} bt_callbacks_t;


static bt_callbacks_t sBluetoothCallbacks = {sizeof(sBluetoothCallbacks),
                                             adapter_state_change_callback,
                                             adapter_properties_callback,
                                             remote_device_properties_callback,
                                             device_found_callback,
											...};

  • HAL_CBACK(bt_hal_cbacks, device_found_cb, num_properties, properties);
    • 调用的就是 om_android_bluetooth_btservice_AdapterService.cpp::device_found_callback
  • HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, status, &bd_addr, num_properties, properties);
    • 调用的就是 om_android_bluetooth_btservice_AdapterService.cpp::remote_device_properties_callback
// android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
static void device_found_callback(int num_properties,
                                  bt_property_t* properties) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;

  ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), NULL);
  int addr_index;
  for (int i = 0; i < num_properties; i++) {
    if (properties[i].type == BT_PROPERTY_BDADDR) {
      addr.reset(sCallbackEnv->NewByteArray(properties[i].len));
      if (!addr.get()) {
        ALOGE("Address is NULL (unable to allocate) in %s", __func__);
        return;
      }
      sCallbackEnv->SetByteArrayRegion(addr.get(), 0, properties[i].len,
                                       (jbyte*)properties[i].val);
      addr_index = i;
    }
  }
  if (!addr.get()) {
    ALOGE("Address is NULL in %s", __func__);
    return;
  }

  ALOGV("%s: Properties: %d, Address: %s", __func__, num_properties,
        (const char*)properties[addr_index].val);

  remote_device_properties_callback(BT_STATUS_SUCCESS,
                                    (RawAddress*)properties[addr_index].val,
                                    num_properties, properties); // 1. 

  sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback,
                               addr.get()); // 回调到 java 层
}
// android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
static void remote_device_properties_callback(bt_status_t status,
                                              RawAddress* bd_addr,
                                              int num_properties,
                                              bt_property_t* properties) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;

  ALOGV("%s: Status is: %d, Properties: %d", __func__, status, num_properties);

  if (status != BT_STATUS_SUCCESS) {
    ALOGE("%s: Status %d is incorrect", __func__, status);
    return;
  }

  ScopedLocalRef<jbyteArray> val(
      sCallbackEnv.get(),
      (jbyteArray)sCallbackEnv->NewByteArray(num_properties));
  if (!val.get()) {
    ALOGE("%s: Error allocating byteArray", __func__);
    return;
  }

  ScopedLocalRef<jclass> mclass(sCallbackEnv.get(),
                                sCallbackEnv->GetObjectClass(val.get()));

  /* Initialize the jobjectArray and jintArray here itself and send the
   initialized array pointers alone to get_properties */

  ScopedLocalRef<jobjectArray> props(
      sCallbackEnv.get(),
      sCallbackEnv->NewObjectArray(num_properties, mclass.get(), NULL));
  if (!props.get()) {
    ALOGE("%s: Error allocating object Array for properties", __func__);
    return;
  }

  ScopedLocalRef<jintArray> types(
      sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_properties));
  if (!types.get()) {
    ALOGE("%s: Error allocating int Array for values", __func__);
    return;
  }

  ScopedLocalRef<jbyteArray> addr(
      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
  if (!addr.get()) {
    ALOGE("Error while allocation byte array in %s", __func__);
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                   (jbyte*)bd_addr);

  jintArray typesPtr = types.get();
  jobjectArray propsPtr = props.get();
  if (get_properties(num_properties, properties, &typesPtr, &propsPtr) < 0) {
    return;
  }

  sCallbackEnv->CallVoidMethod(sJniCallbacksObj,
                               method_devicePropertyChangedCallback, addr.get(),
                               types.get(), props.get()); // 回调到 java 层
}

// android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp

  method_devicePropertyChangedCallback = env->GetMethodID(
      jniCallbackClass, "devicePropertyChangedCallback", "([B[I[[B)V");

  method_deviceFoundCallback =
      env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
// android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
    void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] val) {
        mRemoteDevices.devicePropertyChangedCallback(address, types, val);
    }

    void deviceFoundCallback(byte[] address) {
        mRemoteDevices.deviceFoundCallback(address);
    }
2. RemoteDevices.devicePropertyChangedCallback

是 Java 层对 native 层 method_devicePropertyChangedCallback 的响应回调,用于更新本地记录的远程蓝牙设备属性。

// android/app/src/com/android/bluetooth/btservice/RemoteDevices.java


	/*
		address:远程设备的 MAC 地址(byte[] 格式)
		
		types:属性类型数组(int 值,参照 AbstractionLayer.BT_PROPERTY_* 常量定义)
		
		values:每个属性对应的值(数组形式,一一对应)
	*/
    void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
        Intent intent;
        byte[] val;
        int type;
        BluetoothDevice bdDevice = getDevice(address);
        DeviceProperties deviceProperties;

		/*
			如果是第一次收到该地址设备的属性变更,说明是新设备,需添加。
			
			DeviceProperties 是系统对一个远程设备的本地属性封装类。
		*/
        if (bdDevice == null) {
            debugLog("Added new device property");
            deviceProperties = addDeviceProperties(address); // 创建新的 DeviceProperties
            bdDevice = getDevice(address);
        } else {
            deviceProperties = getDeviceProperties(bdDevice); // 再次获取 BluetoothDevice 实例
        }
		// 无属性则退出。
        if (types.length <= 0) {
            errorLog("No properties to update");
            return;
        }

		// 遍历所有变更的属性
        for (int j = 0; j < types.length; j++) {
            type = types[j];
            val = values[j];
            if (val.length > 0) {
                synchronized (mObject) { // 同步锁:避免并发问题
                    infoLog("Property type: " + type);
                    // 根据属性类型更新具体字段
                    switch (type) {
                        case AbstractionLayer.BT_PROPERTY_BDNAME: // 设备名称
                            final String newName = new String(val);
                            if (newName.equals(deviceProperties.getName())) {
                                infoLog("Skip name update for " + bdDevice);
                                break;
                            }
                            deviceProperties.setName(newName);
                            // 广播设备名称改变事件
                            intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
                            intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName());
                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                            sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
                                    Utils.getTempAllowlistBroadcastOptions());
                            infoLog("Remote Device name is: " + deviceProperties.getName());
                            break;
                        case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME: // 用户自定义名称(Alias)
                            deviceProperties.setAlias(bdDevice, new String(val));
                            infoLog("Remote device alias is: " + deviceProperties.getAlias());
                            break;
                        case AbstractionLayer.BT_PROPERTY_BDADDR: // 设备地址
                            deviceProperties.setAddress(val);
                            infoLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
                            break;
                        // 设备类型标识(例如:手机、耳机)
                        case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE: 
                            final int newClass = Utils.byteArrayToInt(val);
                            if (newClass == deviceProperties.getBluetoothClass()) {
                                infoLog("Skip class update for " + bdDevice);
                                break;
                            }
                            deviceProperties.setBluetoothClass(newClass);
                            // 广播 class 改变事件
                            intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
                            intent.putExtra(BluetoothDevice.EXTRA_CLASS,
                                    new BluetoothClass(deviceProperties.getBluetoothClass()));
                            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                            sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
                                    Utils.getTempAllowlistBroadcastOptions());
                            infoLog("Remote class is:" + newClass);
                            break;
                        case AbstractionLayer.BT_PROPERTY_UUIDS: 
	                        // 支持的 Profile UUID , SDP 发现结束后会通过此字段更新支持的 Profile,如 A2DP、HFP。
                            int numUuids = val.length / AbstractionLayer.BT_UUID_SIZE;
                            final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val);
                            //ParcelUuid[] uuids = updateUuids(deviceProperties.mUuids, newUuids);
                            if (areUuidsEqual(newUuids, deviceProperties.mUuids)) {
                                infoLog( "Skip uuids update for " + bdDevice.getAddress());
                                break;
                            }
                            deviceProperties.mUuids = newUuids;
                            if (sAdapterService.getState() == BluetoothAdapter.STATE_ON) {
                                sAdapterService.deviceUuidUpdated(bdDevice);
                                sendUuidIntent(bdDevice, deviceProperties);
                            } else if (sAdapterService.getState()
                                    == BluetoothAdapter.STATE_BLE_ON) {
                                sAdapterService.deviceUuidUpdated(bdDevice);
                            }
                            break;
                        case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
                            // 设备连接类型(BR/EDR, LE, Dual)
                            if (deviceProperties.isConsolidated()) {
                                break;
                            }
                            // The device type from hal layer, defined in bluetooth.h,
                            // matches the type defined in BluetoothDevice.java
                            deviceProperties.setDeviceType(Utils.byteArrayToInt(val));
                            break;
                        case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI: // 信号强度
                            // RSSI from hal is in one byte
                            deviceProperties.setRssi(val[0]);
                            break;
                        case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER:
		                    // 是否属于 Coordinated Set
                            deviceProperties.setIsCoordinatedSetMember(val[0] != 0);
                            break;
                    }
                }
            }
        }
    }

功能点 说明
📥 入口 native 层通知 Java 层远程设备属性有更新(如名称、class、UUID、RSSI)
🗃️ 存储 更新本地 Java 层缓存(DeviceProperties 对象)
📢 广播 对关键属性(名称、class、UUID)变化发送系统广播
💾 保存 最终可能通过 StorageModule 写入 bt_config.conf(如 UUID、Alias、Class)
🧩 用途 支持 UI 展示、连接判断、profile 支持判断等
3. RemoteDevices.deviceFoundCallback

此函数是在设备被扫描到时由 native 层调用 Java 层,属于蓝牙设备发现流程的重要组成部分。

当蓝牙发现流程(Inquiry 或 LE Scan)中发现了一个新设备或再次发现旧设备时,会触发此回调。它的职责是:

  • 获取设备信息

  • 根据系统配置和策略决定是否广播设备发现

  • 通过 ACTION_FOUND 广播通知系统和应用


// android/app/src/com/android/bluetooth/btservice/RemoteDevices.java

	// Native 层通过 JNI 调用 Java 层,传入远程设备的地址(6 字节 MAC 地址)。
	void deviceFoundCallback(byte[] address) {
        // The device properties are already registered - we can send the intent
        // now 根据 MAC 地址获取或创建 BluetoothDevice 对象。
        BluetoothDevice device = getDevice(address);
        infoLog("deviceFoundCallback: Remote Address is:" + device);

		// 获取设备的本地属性封装对象(DeviceProperties),包含设备名称、class、RSSI 等。
        DeviceProperties deviceProp = getDeviceProperties(device);
        if (deviceProp == null) {
	        // 如果属性为空(很罕见,可能是同步未完成),直接返回。
            errorLog("Device Properties is null for Device:" + device);
            return;
        }
        // 检查是否开启“限制无名称设备广播”策略
        boolean restrict_device_found =
                SystemProperties.getBoolean("bluetooth.restrict_discovered_device.enabled", false);
        if (restrict_device_found && (deviceProp.mName == null || deviceProp.mName.isEmpty())) {
            // 读取系统属性,如果为 true,表示系统不希望广播没有名字的设备(可用于节能或隐私控制)
            // 如果设备没有名字,并且限制策略开启,则不广播此设备。
            debugLog("Device name is null or empty: " + device);
            return;
        }


		/*
			应用层级过滤(如阻止某些设备类型)
		
				filterDevice() 是系统或厂商定制的设备过滤逻辑(如过滤黑名单、特殊厂商设备等)。
				
				若返回 true,跳过此设备广播。
		*/
        if (filterDevice(device)) {
            warnLog("Not broadcast Device: " + device);
            return;
        }

        infoLog("device:" + device + " adapterIndex=" + device.getAdapterIndex());

		// 创建用于通知发现设备的广播事件。
        Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); // 附加设备对象本身。
        intent.putExtra(BluetoothDevice.EXTRA_CLASS,
                new BluetoothClass(deviceProp.getBluetoothClass())); // 附加设备类型(如手机、耳机、电脑等)
        intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.getRssi()); // 附加设备的信号强度。
        intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.getName()); // 附加设备名称。
        intent.putExtra(BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER,
                deviceProp.isCoordinatedSetMember()); // 附加是否属于 Coordinated Set(蓝牙 5.2+ 中用于群组播放,如 TWS 左右耳同时控制)。

		/*
			广播发出
		
				发出带权限限制的广播,只有持有 BLUETOOTH_SCAN 权限的应用可以接收该广播。
				
				使用 sendBroadcastMultiplePermissions() 是对 sendBroadcast() 的扩展,支持多权限、支持临时广播策略(如广播延迟/前台优先级等)。
		*/
        sAdapterService.sendBroadcastMultiplePermissions(intent,
                            new String[] { BLUETOOTH_SCAN },
                            Utils.getTempBroadcastOptions());
    }
阶段 描述
1️⃣ 获取 根据 address 获取 BluetoothDevice 和 DeviceProperties
2️⃣ 检查 是否开启了过滤策略(无名称设备、特定设备过滤)
3️⃣ 构建 创建广播 intent 并附加设备属性
4️⃣ 广播 向系统发送 ACTION_FOUND 广播,仅供授权应用接收

应用场景:

  • 当用户在设置界面打开蓝牙并点击“扫描设备”时,后台会多次触发 deviceFoundCallback()。
  • App 中注册了 ACTION_FOUND 广播接收器后,可以接收到附近设备并展示在列表上。
4. 小结

协议栈 native 侧,会触发上面的两路 回调, 但是他们 所代表 的含义却是不同的:

  • invoke_device_found_cb[native] -> RemoteDevices.deviceFoundCallback[java]
  • invoke_remote_device_properties_cb[native] -> RemoteDevices.devicePropertyChangedCallback[java]

devicePropertyChangedCallbackdeviceFoundCallback 是 AOSP 蓝牙框架中两个核心的回调函数,虽然它们都与设备属性和发现有关,但它们在触发时机、作用、广播内容、应用场景等方面都有明显差异。

下面从多个维度对 相同点与不同点 进行详细对比:


1.相同点
维度 描述
🔧 来源 都是由 native 层(通过 JNI)调用 Java 层的回调函数
📡 与设备相关 都涉及对某个 BluetoothDevice 设备的处理
🧠 依赖 DeviceProperties 都通过 getDeviceProperties(device) 获取设备缓存属性
🔒 权限控制 广播时都依赖蓝牙相关权限(如 BLUETOOTH_SCAN
📲 可导致广播 都有可能向上层发送 Android 广播(如 ACTION_FOUND, ACTION_NAME_CHANGED, ACTION_UUID, 等)
🧪 开发调试中常出现 都会在使用蓝牙调试(如配对、扫描)过程中频繁触发
🧩 与应用层交互 都可能引发第三方 app 的回调(通过广播接收器)

2.不同点
比较维度 deviceFoundCallback devicePropertyChangedCallback
💥 触发时机 当蓝牙扫描发现设备时调用(第一次或再次发现) 当远程设备的属性发生变化时调用(如名称、RSSI、UUID 等)
🔁 调用频率 在一次扫描过程中可能多次触发(每个设备发现一次) 属性每变化一次触发一次,可能频繁(如 RSSI 不断变化)
📩 广播行为 广播 BluetoothDevice.ACTION_FOUND(设备被发现) 根据属性类型广播不同事件,如:
  • ACTION_NAME_CHANGED
  • ACTION_UUID
  • ACTION_RSSI
🔍 目的 表示“新设备”被发现,通知系统和应用显示 表示“已知设备的属性”发生变化,更新状态或 UI
🧬 广播携带信息 BluetoothDevice、设备类型、名称、RSSI、是否为群组成员 取决于变化的属性(可能是 UUID、名称、RSSI)
🧰 过滤策略参与 参与“是否广播”策略(如名称为空不广播) 不参与过滤,始终处理属性变化
📲 典型场景 蓝牙设置页中设备扫描列表展示 已配对设备列表中设备名称、信号变化,或配对时获取 UUID
💡 是否依赖扫描流程 是,仅在蓝牙扫描流程中调用 否,也可能在连接、配对、服务发现后调用

3.实际生活中的类比
情况 deviceFoundCallback devicePropertyChangedCallback
你在商场里发现一个新品牌店铺 店铺出现在你面前的那一刻 —— “发现设备” 店铺换了名字、装修风格变了、换老板了、上了新的商品 —— “属性改变”
手机蓝牙设置中扫描时发现设备列表刷新 每个设备出现一次触发一次 某设备名称更新或信号强度变化,刷新其展示项

4.应用开发建议
目标 使用哪个回调
想监听设备是否被发现(用于展示设备列表) BluetoothDevice.ACTION_FOUND 广播(源自 deviceFoundCallback
想监听某设备名称是否变更(如设备重命名) BluetoothDevice.ACTION_NAME_CHANGED(源自 devicePropertyChangedCallback
想获取设备的 UUID(服务)更新 BluetoothDevice.ACTION_UUID(源自 devicePropertyChangedCallback
想实时显示设备信号强度(如附近蓝牙设备距离) BluetoothDevice.EXTRA_RSSI(由 devicePropertyChangedCallback 中 RSSI 更新引发)

3. 系统接口

关于 DevicePropertyNative 有如下几个接口:

  • android/app/src/com/android/bluetooth/btservice/AdapterService.java

    /*package*/
    native boolean setDevicePropertyNative(byte[] address, int type, byte[] val);

    /*package*/
    native boolean getDevicePropertyNative(byte[] address, int type);

1. Get 流程

getDevicePropertyNative

android/app/src/com/android/bluetooth/btservice/AdapterService.java
    /*package*/
    native boolean getDevicePropertyNative(byte[] address, int type);
// android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
static jboolean getDevicePropertyNative(JNIEnv* env, jobject obj,
                                        jbyteArray address, jint type) {
  ALOGV("%s", __func__);

  if (!sBluetoothInterface) return JNI_FALSE;

  jbyte* addr = env->GetByteArrayElements(address, NULL);
  if (addr == NULL) {
    jniThrowIOException(env, EINVAL);
    return JNI_FALSE;
  }

  int ret = sBluetoothInterface->get_remote_device_property(
      (RawAddress*)addr, (bt_property_type_t)type);
  env->ReleaseByteArrayElements(address, addr, 0);
  return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
system/btif/src/bluetooth.cc

int get_remote_device_property(RawAddress* remote_addr,
                               bt_property_type_t type) {
  if (!btif_is_enabled()) return BT_STATUS_NOT_READY;

  do_in_main_thread(FROM_HERE, base::BindOnce(btif_get_remote_device_property,
                                              *remote_addr, type));
  return BT_STATUS_SUCCESS;
}
system/btif/src/btif_core.cc

/*******************************************************************************
 *
 * Function         btif_get_remote_device_property
 *
 * Description      Fetches the remote device property from the NVRAM
 *
 ******************************************************************************/
void btif_get_remote_device_property(RawAddress remote_addr,
                                     bt_property_type_t type) {
  char buf[1024];
  bt_property_t prop;
  prop.type = type;
  prop.val = (void*)buf;
  prop.len = sizeof(buf);

  bt_status_t status =
      btif_storage_get_remote_device_property(&remote_addr, &prop);
  invoke_remote_device_properties_cb(status, remote_addr, 1, &prop); // 1. 
}

invoke_remote_device_properties_cb


// system/btif/src/btif_core.cc
void btif_remote_properties_evt(bt_status_t status, RawAddress* remote_addr,
                                uint32_t num_props, bt_property_t* p_props) {
  invoke_remote_device_properties_cb(status, *remote_addr, num_props, p_props);
}

2. 写配置

btif_storage_add_remote_device

system/btif/src/btif_storage.cc

bt_status_t btif_storage_add_remote_device(const RawAddress* remote_bd_addr,
                                           uint32_t num_properties,
                                           bt_property_t* properties) {
  uint32_t i = 0;
  /* TODO: If writing a property, fails do we go back undo the earlier
   * written properties? */
  for (i = 0; i < num_properties; i++) {
    /* Ignore the RSSI as this is not stored in DB */
    if (properties[i].type == BT_PROPERTY_REMOTE_RSSI) continue;

    /* address for remote device needs special handling as we also store
     * timestamp */
    if (properties[i].type == BT_PROPERTY_BDADDR) {
      bt_property_t addr_prop;
      memcpy(&addr_prop, &properties[i], sizeof(bt_property_t));
      addr_prop.type = (bt_property_type_t)BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP;
      btif_storage_set_remote_device_property(remote_bd_addr, &addr_prop); // 1. 
    } else {
      btif_storage_set_remote_device_property(remote_bd_addr, &properties[i]); // 2. 
    }
  }
  return BT_STATUS_SUCCESS;
}

btif_storage_set_remote_device_property

// system/btif/src/btif_storage.cc
bt_status_t btif_storage_set_remote_device_property(
    const RawAddress* remote_bd_addr, bt_property_t* property) {
  return prop2cfg(remote_bd_addr, property) ? BT_STATUS_SUCCESS
                                            : BT_STATUS_FAIL;
}

prop2cfg


static int prop2cfg(const RawAddress* remote_bd_addr, bt_property_t* prop) {
  std::string bdstr;
  if (remote_bd_addr) {
    bdstr = remote_bd_addr->ToString();
  }

  char value[1024];
  if (prop->len <= 0 || prop->len > (int)sizeof(value) - 1) {
    LOG_WARN(
        "Unable to save property to configuration file type:%d, "
        " len:%d is invalid",
        prop->type, prop->len);
    return false;
  }
  switch (prop->type) {
    case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_DEVTIME,
                          (int)time(NULL));
      break;
    case BT_PROPERTY_BDNAME: {
      int name_length = prop->len > BTM_MAX_LOC_BD_NAME_LEN
                            ? BTM_MAX_LOC_BD_NAME_LEN
                            : prop->len;
      strncpy(value, (char*)prop->val, name_length);
      value[name_length] = '\0';
      if (remote_bd_addr) {
        btif_config_set_str(bdstr, BTIF_STORAGE_PATH_REMOTE_NAME, value);
      } else {
        btif_config_set_str("Adapter", BTIF_STORAGE_KEY_ADAPTER_NAME, value);
        btif_config_flush();
      }
      break;
    }
    case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
      strncpy(value, (char*)prop->val, prop->len);
      value[prop->len] = '\0';
      btif_config_set_str(bdstr, BTIF_STORAGE_PATH_REMOTE_ALIASE, value);
      break;
    case BT_PROPERTY_ADAPTER_SCAN_MODE:
      btif_config_set_int("Adapter", BTIF_STORAGE_KEY_ADAPTER_SCANMODE,
                          *(int*)prop->val);
      break;
    case BT_PROPERTY_LOCAL_IO_CAPS:
      btif_config_set_int("Adapter", BTIF_STORAGE_KEY_LOCAL_IO_CAPS,
                          *(int*)prop->val);
      break;
    case BT_PROPERTY_LOCAL_IO_CAPS_BLE:
      btif_config_set_int("Adapter", BTIF_STORAGE_KEY_LOCAL_IO_CAPS_BLE,
                          *(int*)prop->val);
      break;
    case BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
      btif_config_set_int("Adapter", BTIF_STORAGE_KEY_ADAPTER_DISC_TIMEOUT,
                          *(int*)prop->val);
      break;
    case BT_PROPERTY_CLASS_OF_DEVICE:
      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_DEVCLASS,
                          *(int*)prop->val);
      break;
    case BT_PROPERTY_TYPE_OF_DEVICE:
      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_DEVTYPE,
                          *(int*)prop->val);
      break;
    case BT_PROPERTY_UUIDS: {
      std::string val;
      size_t cnt = (prop->len) / sizeof(Uuid);
      for (size_t i = 0; i < cnt; i++) {
        val += (reinterpret_cast<Uuid*>(prop->val) + i)->ToString() + " ";
      }
      btif_config_set_str(bdstr, BTIF_STORAGE_PATH_REMOTE_SERVICE, val);
      break;
    }
    case BT_PROPERTY_REMOTE_VERSION_INFO: {
      bt_remote_version_t* info = (bt_remote_version_t*)prop->val;

      if (!info) return false;

      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_MFCT,
                          info->manufacturer);
      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_VER, info->version);
      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_SUBVER,
                          info->sub_ver);
    } break;

    default:
      BTIF_TRACE_ERROR("Unknown prop type:%d", prop->type);
      return false;
  }

  /* No need to look for bonded device with address of NULL */
  if (remote_bd_addr &&
      btif_in_fetch_bonded_device(bdstr) == BT_STATUS_SUCCESS) {
    /* save changes if the device was bonded */
    btif_config_flush();
  }

  return true;
}
1. btif_config_set_str 和 btif_config_set_int
// system/btif/src/btif_config.cc
bool btif_config_set_int(const std::string& section, const std::string& key,
                         int value) {
  CHECK(bluetooth::shim::is_gd_stack_started_up());
  return bluetooth::shim::BtifConfigInterface::SetInt(section, key, value);
}


// system/main/shim/config.cc
bool BtifConfigInterface::SetInt(const std::string& section,
                                 const std::string& property, int value) {
  ConfigCacheHelper::FromConfigCache(*GetStorage()->GetConfigCache())
      .SetInt(section, property, value);
  return true;
}

// system/gd/storage/config_cache_helper.cc
void ConfigCacheHelper::SetInt(const std::string& section, const std::string& property, int value) {
  config_cache_.SetProperty(section, property, std::to_string(value));
}
2. ConfigCache::SetProperty

功能:

  • 设置一个配置项(如蓝牙设备属性或通用配置),并根据是否为设备属性决定是否进入持久化配置或临时配置缓存中。
// system/gd/storage/config_cache.cc

void ConfigCache::SetProperty(std::string section, std::string property, std::string value) {
  /*
	使用递归互斥锁保护对 information_sections_、persistent_devices_、temporary_devices_ 等共享数据结构的并发访问。

	防止多线程同时读写 config。
  */
  std::lock_guard<std::recursive_mutex> lock(mutex_);


  // 移除传入字符串中可能存在的 \n 或 \r,防止注入或破坏配置格式。
  TrimAfterNewLine(section);
  TrimAfterNewLine(property);
  TrimAfterNewLine(value);


  // section 和 property 名不能为空,否则断言失败(开发期调试用)
  ASSERT_LOG(!section.empty(), "Empty section name not allowed");
  ASSERT_LOG(!property.empty(), "Empty property name not allowed");

  /* 判断是否为“设备节
		一般如 [Adapter], [Metrics], [Global] 属于非设备配置节;

		而类似 [Device_XX:XX:XX:XX:XX:XX] 才是设备配置节。
	*/
  if (!IsDeviceSection(section)) {
    /*
	    存入 information_sections_(非设备类配置)
    
			如果该节尚不存在,则创建;

				将 property -> value 插入;

				通知配置已更改。

				适用于如 [Adapter] 节下的 Name、ScanMode 等普通配置项。
    */
    auto section_iter = information_sections_.find(section);
    if (section_iter == information_sections_.end()) {
      section_iter = information_sections_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
    }
    section_iter->second.insert_or_assign(property, std::move(value));
    PersistentConfigChangedCallback();
    return;
  }

  /*
	  如果是设备配置节(Device_...),检查是否可持久化保存
		  如果该设备还不在 persistent_devices_ 中,且当前 property 是可持久化的(如 LinkKey),尝试从临时设备中迁移。
		  只有配对时,才有可能将 设备 从临时设备列表中, 移到 可持久化列表里。
  */
  auto section_iter = persistent_devices_.find(section);
  if (section_iter == persistent_devices_.end() && IsPersistentProperty(property)) {
    // move paired devices or create new paired device when a link key is set


	/*
		从 temporary_devices_ 迁移或新建一项

			若该设备的属性存在于临时设备列表中,则将其“转正”迁入 persistent;

			否则新建。

			场景举例:连接配对时第一次保存 link key,从临时状态迁移为持久配对状态。
	*/
    auto section_properties = temporary_devices_.extract(section);
    if (section_properties) {
      section_iter = persistent_devices_.try_emplace_back(section, std::move(section_properties->second)).first;
    } else {
      section_iter = persistent_devices_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
    }
  }

/*
	安全模式下加密敏感属性值

		如果开启了安全模式(如 CC Mode)并且属性为敏感字段(如 LinkKey、LE_KEY_PENC 等):

			通过 KeyStore 接口尝试加密存储;

			若成功,则设置为标记字符串 value = "$encrypted" 表示已加密。
*/
  if (section_iter != persistent_devices_.end()) {
    bool is_encrypted = value == kEncryptedStr;
    if ((!value.empty()) && os::ParameterProvider::GetBtKeystoreInterface() != nullptr &&
        os::ParameterProvider::IsCommonCriteriaMode() && InEncryptKeyNameList(property) && !is_encrypted) {
      if (os::ParameterProvider::GetBtKeystoreInterface()->set_encrypt_key_or_remove_key(
              section + "-" + property, value)) {
        value = kEncryptedStr;
      }
    }


/*
	插入持久化设备属性 + 通知更改

		将处理后的值插入设备节中;

		通知持久化更改(通常触发异步写入 config 文件)。
*/
    section_iter->second.insert_or_assign(property, std::move(value));
    PersistentConfigChangedCallback();
    return;
  }

/*
	如果该设备节仍不存在,写入 temporary_devices_

		表示当前属性不需要持久化;

		保存到 temporary_devices_ 中(通常用于会话属性、未配对设备等  );

		扫描到的设备都一般存储在这里

*/
  section_iter = temporary_devices_.find(section);
  if (section_iter == temporary_devices_.end()) {
    auto triple = temporary_devices_.try_emplace(section, common::ListMap<std::string, std::string>{});
    section_iter = std::get<0>(triple);
  }
  section_iter->second.insert_or_assign(property, std::move(value));
}

在搜素时,没有配对的设备 都将保存在 临时 设备列表里面。

只有 在配对时,收到 设备的 LinkKey 时, 才会从临时设备 列表,挪到 可持久设备列表里面。

  /*
	  如果是设备配置节(Device_...),检查是否可持久化保存
		  如果该设备还不在 persistent_devices_ 中,且当前 property 是可持久化的(如 LinkKey),尝试从临时设备中迁移。
		  只有配对时,才有可能将 设备 从临时设备列表中, 移到 可持久化列表里。
  */
  auto section_iter = persistent_devices_.find(section);
  if (section_iter == persistent_devices_.end() && IsPersistentProperty(property)) {
    // move paired devices or create new paired device when a link key is set
	...
	}
1. IsPersistentProperty(property)
// system/gd/storage/config_cache.cc

bool ConfigCache::IsPersistentProperty(const std::string& property) const {
  return persistent_property_names_.find(property) != persistent_property_names_.end();
}


ConfigCache::ConfigCache(size_t temp_device_capacity, std::unordered_set<std::string_view> persistent_property_names)
    : persistent_property_names_(std::move(persistent_property_names)),
      information_sections_(),
      persistent_devices_(),
      temporary_devices_(temp_device_capacity) {}
  • 在 ConfigCache 构造函数里面,会初始化 persistent_property_names_
// system/gd/storage/storage_module.cc
void StorageModule::Start() {
	...
	auto config = LegacyConfigFile::FromPath(config_file_path_).Read(temp_devices_capacity_);
	...
}
// system/gd/storage/legacy_config_file.cc

std::optional<ConfigCache> LegacyConfigFile::Read(size_t temp_devices_capacity) {
  ...
  ConfigCache cache(temp_devices_capacity, Device::kLinkKeyProperties);
  ...
}

在 StorageModule 模块初始化时, 将 从 /data/misc/bluedroid/bt_config.conf 读取内容, 此时会 创建 ConfigCache 对象,此时 persistent_property_names_ = Device::kLinkKeyProperties

4. 在各阶段的核心作用


1. 搜索阶段(Inquiry / BLE Scan)

1. 作用

  • 当 Controller 发现新设备时(classic 或 BLE):

    • 没有就创建并初始化
    • 有就更新(如 RSSI、名称、设备类型)
  • 作用于:

    • 避免重复设备出现在 UI(去重)
    • 通知 Java 层刷新设备列表

2. 解决的问题

问题 如何解决
同一设备出现多次 用地址唯一标识缓存
设备名称/类型不一致 统一由 DeviceProperties 更新

3. BR/EDR 扫描流程鉴赏

1. btif_dm_search_devices_evt

这段代码是 设备发现流程的核心处理逻辑之一,对应于系统层调用 BluetoothAdapter.startDiscovery() 时产生的设备发现过程中的事件处理。

btif_dm_search_devices_evt() 是 BTIF 层处理 BTA 层 tBTA_DM_xxx_EVT 事件的统一入口:

  • 当发现远程设备时(设备通过 Inquiry 过程或 BLE 扫描被发现),event == BTA_DM_INQ_RES_EVT,该事件就会触发,并进入 case 分支处理。
// system/btif/src/btif_dm.cc
static void btif_dm_search_devices_evt(tBTA_DM_SEARCH_EVT event,
                                       tBTA_DM_SEARCH* p_search_data) {
  BTIF_TRACE_EVENT("%s event=%s", __func__, dump_dm_search_event(event));

  switch (event) {
  ...
    case BTA_DM_INQ_RES_EVT: {
      /* inquiry result */
      bt_bdname_t bdname;
      uint8_t remote_name_len;
      uint8_t num_uuids = 0, num_uuids128 = 0, max_num_uuid = 32;
      uint8_t uuid_list[32 * Uuid::kNumBytes16];
      uint8_t uuid_list128[32 * Uuid::kNumBytes128];

      p_search_data->inq_res.remt_name_not_required =
          check_eir_remote_name(p_search_data, NULL, NULL);
      RawAddress& bdaddr = p_search_data->inq_res.bd_addr;

	  //  打印发现设备的地址和类型(BR/EDR、BLE 或 DUMO),有助于调试日志追踪。
      BTIF_TRACE_DEBUG("%s() %s device_type = 0x%x\n", __func__,
                       bdaddr.ToString().c_str(),
                       p_search_data->inq_res.device_type);
      bdname.name[0] = 0;

      // 优先尝试从 EIR(Extended Inquiry Response)中解析远程设备名称,如果没有,就尝试从缓存中找
      if (!check_eir_remote_name(p_search_data, bdname.name, &remote_name_len))
        check_cached_remote_name(p_search_data, bdname.name, &remote_name_len);

      /* Check EIR for services */
      // 提取设备 EIR 中的 UUID(服务列表)
      if (p_search_data->inq_res.p_eir) {
        // 分别提取 16-bit 和 128-bit UUID,用于构建远程设备所支持的服务,比如 A2DP Sink、HID、PAN 等
        BTM_GetEirUuidList(p_search_data->inq_res.p_eir,
                           p_search_data->inq_res.eir_len, Uuid::kNumBytes16,
                           &num_uuids, uuid_list, max_num_uuid);

        BTM_GetEirUuidList(p_search_data->inq_res.p_eir,
                           p_search_data->inq_res.eir_len, Uuid::kNumBytes128,
                           &num_uuids128, uuid_list128, max_num_uuid);
      }

      {
        // 组织设备属性结构体 bt_property_t
        bt_property_t properties[7];
        bt_device_type_t dev_type;
        uint32_t num_properties = 0;
        bt_status_t status;
        tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC;

        memset(properties, 0, sizeof(properties));
        // 下面依次填充不同类型的属性:
        
        /* RawAddress */
        BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                   BT_PROPERTY_BDADDR/*设备地址*/, sizeof(bdaddr), &bdaddr);
        num_properties++;
        /* BD_NAME */
        /* Don't send BDNAME if it is empty */
        if (bdname.name[0]) {
          BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                     BT_PROPERTY_BDNAME/*设备名称(非空时填充)*/,
                                     strlen((char*)bdname.name), &bdname);
          num_properties++;
        }

        /* DEV_CLASS */
        uint32_t cod = devclass2uint(p_search_data->inq_res.dev_class);
        BTIF_TRACE_DEBUG("%s cod is 0x%06x", __func__, cod);
        if (cod != 0) {
          BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                     BT_PROPERTY_CLASS_OF_DEVICE/*COD 设备类别*/, sizeof(cod),
                                     &cod);
          num_properties++;
        }

        /* DEV_TYPE */
        /* FixMe: Assumption is that bluetooth.h and BTE enums match */

        /* Verify if the device is dual mode in NVRAM */
        int stored_device_type = 0;
        if (btif_get_device_type(bdaddr, &stored_device_type) &&
            ((stored_device_type != BT_DEVICE_TYPE_BREDR &&
              p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BREDR) ||
             (stored_device_type != BT_DEVICE_TYPE_BLE &&
              p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE))) {
          dev_type = (bt_device_type_t)BT_DEVICE_TYPE_DUMO;
        } else {
          dev_type = (bt_device_type_t)p_search_data->inq_res.device_type;
        }

        if (p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE)
          addr_type = p_search_data->inq_res.ble_addr_type;
        BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                   BT_PROPERTY_TYPE_OF_DEVICE/*设备类型(BR/EDR、BLE、DUMO)*/, sizeof(dev_type),
                                   &dev_type);
        num_properties++;
        /* RSSI */
        BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                   BT_PROPERTY_REMOTE_RSSI/*信号强度*/, sizeof(int8_t),
                                   &(p_search_data->inq_res.rssi));
        num_properties++;

        /* CSIP supported device */
        BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                   BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER/*是否为 Coordinated Set(例如耳机双边同步)*/,
                                   sizeof(bool),
                                   &(p_search_data->inq_res.include_rsi));
        num_properties++;

        /* Cache EIR queried services */
        if ((num_uuids + num_uuids128) > 0) {
          uint16_t* p_uuid16 = (uint16_t*)uuid_list;
          auto uuid_iter = eir_uuids_cache.find(bdaddr);
          Uuid    new_remote_uuid[BT_MAX_NUM_UUIDS];
          size_t  dst_max_num = sizeof(new_remote_uuid)/sizeof(Uuid);
          size_t  new_num_uuid = 0;
          Uuid    remote_uuid[BT_MAX_NUM_UUIDS];

          if (uuid_iter == eir_uuids_cache.end()) {
            auto triple = eir_uuids_cache.try_emplace(bdaddr, std::set<Uuid>{});
            uuid_iter = std::get<0>(triple);
          }
          //LOG_INFO("EIR UUIDs for %s:", bdaddr.ToString().c_str());
          for (int i = 0; i < num_uuids; ++i) {
            Uuid uuid = Uuid::From16Bit(p_uuid16[i]);
            //LOG_INFO("        %s", uuid.ToString().c_str());
            uuid_iter->second.insert(uuid);

            if (i < BT_MAX_NUM_UUIDS) {
              remote_uuid[i] = uuid;
            } else {
              LOG_INFO("%d >= %d", i, BT_MAX_NUM_UUIDS);
            }
          }

          for (int i = 0; i < num_uuids128; ++i) {
            Uuid uuid = Uuid::From128BitBE((uint8_t *)&uuid_list128[i * Uuid::kNumBytes128]);
            //LOG_INFO("        %s", uuid.ToString().c_str());
            uuid_iter->second.insert(uuid);
            if (i < BT_MAX_NUM_UUIDS) {
              remote_uuid[num_uuids + i] = uuid;
            } else {
              LOG_INFO("%d >= %d", i, BT_MAX_NUM_UUIDS);
            }
          }

          //LOG_INFO("%s %d : update EIR UUIDs.", __func__, __LINE__);

          new_num_uuid = btif_update_uuid(bdaddr, remote_uuid,
                                          (num_uuids + num_uuids128), new_remote_uuid,
                                          sizeof(new_remote_uuid),
                                          dst_max_num);
          BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                     BT_PROPERTY_UUIDS/*支持的服务 UUID 列表*/,
                                     new_num_uuid * Uuid::kNumBytes128, new_remote_uuid);
          //LOG_INFO("%s %d : fill BT_PROPERTY_UUIDS property.", __func__, __LINE__);
          num_properties ++;
        }

		// 存储到 内存中, 并更新地址类型, 将新发现的设备保存到本地数据库中,供配对或后续连接使用。
        status =
            btif_storage_add_remote_device(&bdaddr, num_properties, properties);
        ASSERTC(status == BT_STATUS_SUCCESS,
                "failed to save remote device (inquiry)", status);
        status = btif_storage_set_remote_addr_type(&bdaddr, addr_type);
        ASSERTC(status == BT_STATUS_SUCCESS,
                "failed to save remote addr type (inquiry)", status);

		//  可选过滤(非 connectable 的 BLE 设备忽略), 防止显示无法连接的 BLE 设备,例如 Beacon,这对某些厂商有定制用途。
        bool restrict_report = osi_property_get_bool(
            "bluetooth.restrict_discovered_device.enabled", false);
        if (restrict_report &&
            p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE &&
            !(p_search_data->inq_res.ble_evt_type & BTM_BLE_CONNECTABLE_MASK)) {
          LOG_INFO("%s: Ble device is not connectable",
                   bdaddr.ToString().c_str());
          break;
        }

        /* Callback to notify upper layer of device */
        invoke_device_found_cb(num_properties, properties); // 通知上层 Java 层 deviceFoundCallback()
      }
    } break;

  }
}

btif_dm_search_devices_evt() 是 AOSP 中实现设备搜索发现的核心逻辑,负责:

  • 从 Inquiry/BLE 扫描结果中提取远程设备信息;

  • 尝试从 EIR 中解析远程名称和服务;

  • 构建标准的 bt_property_t 属性数组;

  • 存储发现设备并通知 Java 层应用。

而它最终触发的 deviceFoundCallback() 是应用层感知新设备发现的关键入口。

我们再次 devicePropertyChangedCallback vs. deviceFoundCallback 对比:

比较点 devicePropertyChangedCallback deviceFoundCallback
触发条件 已知设备属性变化(如名称变化、RSSI 变化) 发现新设备(搜索阶段)
回调数据 变化的 bt_property_t 属性 初次发现设备的 bt_property_t 全集
使用场景 设备连接后属性更新 搜索设备时通知发现
是否多次触发 是(属性有变化就会触发) 是(发现多个设备时各触发一次)
上层应用行为 更新已有设备界面信息 新增列表项,展示设备名称等

4. Ble scan 时

当 ble 扫描到设备时, 就会触发调用 BleScannerInterfaceImpl::OnScanResult

// system/main/shim/le_scanning_manager.cc

void BleScannerInterfaceImpl::OnScanResult(
    uint16_t event_type, uint8_t address_type, bluetooth::hci::Address address,
    uint8_t primary_phy, uint8_t secondary_phy, uint8_t advertising_sid,
    int8_t tx_power, int8_t rssi, uint16_t periodic_advertising_interval,
    std::vector<uint8_t> advertising_data) {
  RawAddress raw_address = ToRawAddress(address);
  tBLE_ADDR_TYPE ble_addr_type = to_ble_addr_type(address_type);

  if (ble_addr_type != BLE_ADDR_ANONYMOUS) {
    btm_ble_process_adv_addr(raw_address, &ble_addr_type);
  }

  // 这里会 去触发 更新 ble 设备的 properties
  do_in_jni_thread(
      FROM_HERE,
      base::BindOnce(&BleScannerInterfaceImpl::handle_remote_properties,
                     base::Unretained(this), raw_address, ble_addr_type,
                     advertising_data));

  do_in_jni_thread(
      FROM_HERE,
      base::BindOnce(&ScanningCallbacks::OnScanResult,
                     base::Unretained(scanning_callbacks_), event_type,
                     static_cast<uint8_t>(address_type), raw_address,
                     primary_phy, secondary_phy, advertising_sid, tx_power,
                     rssi, periodic_advertising_interval, advertising_data));
  ...
}
// system/main/shim/le_scanning_manager.cc

void BleScannerInterfaceImpl::handle_remote_properties(
    RawAddress bd_addr, tBLE_ADDR_TYPE addr_type,
    std::vector<uint8_t> advertising_data) {
  if (!bluetooth::shim::is_gd_stack_started_up()) {
    LOG_WARN("Gd stack is stopped, return");
    return;
  }

  // skip anonymous advertisment
  if (addr_type == BLE_ADDR_ANONYMOUS) {
    return;
  }

  auto device_type = bluetooth::hci::DeviceType::LE;
  uint8_t flag_len;
  const uint8_t* p_flag = AdvertiseDataParser::GetFieldByType(
      advertising_data, BTM_BLE_AD_TYPE_FLAG, &flag_len);
  if (p_flag != NULL && flag_len != 0) {
    if ((BTM_BLE_BREDR_NOT_SPT & *p_flag) == 0) {
      device_type = bluetooth::hci::DeviceType::DUAL;
    }
  }

  uint8_t remote_name_len;
  const uint8_t* p_eir_remote_name = AdvertiseDataParser::GetFieldByType(
      advertising_data, HCI_EIR_COMPLETE_LOCAL_NAME_TYPE, &remote_name_len);

  if (p_eir_remote_name == NULL) {
    p_eir_remote_name = AdvertiseDataParser::GetFieldByType(
        advertising_data, HCI_EIR_SHORTENED_LOCAL_NAME_TYPE, &remote_name_len);
  }

  // update device name
  if ((addr_type != BLE_ADDR_RANDOM) || (p_eir_remote_name)) {
    if (!address_cache_.find(bd_addr)) {
      address_cache_.add(bd_addr);

      if (p_eir_remote_name) {


        bt_bdname_t bdname;
        memcpy(bdname.name, p_eir_remote_name, remote_name_len);
        if (remote_name_len < BD_NAME_LEN + 1)
          bdname.name[remote_name_len] = '\0';

		// 这里很关键, 也就是说 只有 扫描到的 ble 设备 有名字, 才会去更新 properties. 其他例如 ble 的 rssi  等,都不会更新。
        btif_dm_update_ble_remote_properties(bd_addr, bdname.name, device_type);
      }
    }
  }
  auto* storage_module = bluetooth::shim::GetStorage();
  bluetooth::hci::Address address = ToGdAddress(bd_addr);

  // update device type
  auto mutation = storage_module->Modify();
  bluetooth::storage::Device device =
      storage_module->GetDeviceByLegacyKey(address);
  mutation.Add(device.SetDeviceType(device_type));
  mutation.Commit();

  // update address type
  auto mutation2 = storage_module->Modify();
  bluetooth::storage::LeDevice le_device = device.Le();
  mutation2.Add(
      le_device.SetAddressType((bluetooth::hci::AddressType)addr_type));
  mutation2.Commit();
}
// system/btif/src/btif_dm.cc
void btif_dm_update_ble_remote_properties(const RawAddress& bd_addr,
                                          BD_NAME bd_name,
                                          tBT_DEVICE_TYPE dev_type) {
  btif_update_remote_properties(bd_addr, bd_name, NULL, dev_type);
}
// system/btif/src/btif_dm.cc

static void btif_update_remote_properties(const RawAddress& bdaddr,
                                          BD_NAME bd_name, DEV_CLASS dev_class,
                                          tBT_DEVICE_TYPE device_type) {
  int num_properties = 0;
  bt_property_t properties[3];
  bt_status_t status = BT_STATUS_UNHANDLED;
  uint32_t cod;
  bt_device_type_t dev_type;

  memset(properties, 0, sizeof(properties));

  /* remote name */
  if (strlen((const char*)bd_name)) {
    BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties], BT_PROPERTY_BDNAME,
                               strlen((char*)bd_name), bd_name);
    if (!bluetooth::shim::is_gd_security_enabled()) {
      status = btif_storage_set_remote_device_property(
          &bdaddr, &properties[num_properties]);
      ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote device name",
              status);
    }
    num_properties++;
  }

  ...

  invoke_remote_device_properties_cb(status, bdaddr, num_properties,
                                     properties); // 1. 
}

ble 在扫描阶段, 只有扫描到 设备的名字时, 才会去 调用 invoke_remote_device_properties_cb 更新 properties.

1.例子一
01-02 14:20:35.237345  2146  2634 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:637 btif_update_remote_properties: cod from storage is also unclassified

01-02 14:20:35.237477  2146  2634 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btif_in_fetch_bonded_ble_device Found a LE device: ba:03:cc:1c:cd:22

01-02 14:20:35.237906  2146  2634 D BtGatt.GattService: onScanResult() - eventType=0x1b, addressType=0, address=BA:03:CC:1C:CD:22, primaryPhy=1, secondaryPhy=0, advertisingSid=0xff, txPower=127, rssi=-73, periodicAdvInt=0x0, originalAddress=00:00:00:00:00:00


01-02 14:20:35.238671  2146  2634 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: operator(): HAL bt_hal_cbacks->remote_device_properties_cb
01-02 14:20:35.238841  2146  2634 D BluetoothRemoteDevices: Added new device property
01-02 14:20:35.239122  2146  2634 I BluetoothRemoteDevices: Property type: 1
01-02 14:20:35.240002  2146  2634 I BluetoothRemoteDevices: Remote Device name is: MXD57_MI
01-02 14:20:35.240042  2146  2634 I BluetoothRemoteDevices: Property type: 4
01-02 14:20:35.240082  2146  2634 I BluetoothRemoteDevices: Skip class update for BA:03:CC:1C:CD:22
01-02 14:20:35.240104  2146  2634 I BluetoothRemoteDevices: Property type: 5
131	2025-01-02 14:20:35.236141	controller	host	HCI_EVT	60	Rcvd LE Meta (LE Extended Advertising Report)

Frame 131: 60 bytes on wire (480 bits), 60 bytes captured (480 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI Event - LE Meta
    Event Code: LE Meta (0x3e)
    Parameter Total Length: 57
    Sub Event: LE Extended Advertising Report (0x0d)
    Num Reports: 1
    Event Type: 0x0013, Connectable, Scannable, Legacy, Data Status: Complete
    Peer Address Type: Public Device Address (0x00)
    BD_ADDR: ba:03:cc:1c:cd:22 (ba:03:cc:1c:cd:22)
    Primary PHY: LE 1M (0x01)
    Secondary PHY: No packets on the secondary advertising channel (0x00)
    Advertising SID: 0xff (not available)
    TX Power: 127 dBm (not available)
    RSSI: -74 dBm
    Periodic Advertising Interval: 0x0000 (no periodic advertising)
    Direct Address Type: Public Device Address (0x00)
    Direct BD_ADDR: 00:00:00_00:00:00 (00:00:00:00:00:00)
    Data Length: 31
    Advertising Data
        Flags
        Manufacturer Specific
        Device Name: MXD57_MI  # 广播数据中有设备名字
        Manufacturer Specific


2.例子二
01-02 14:20:35.311975  2146  2634 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:637 btif_update_remote_properties: cod from storage is also unclassified

01-02 14:20:35.312013  2146  2634 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btif_in_fetch_bonded_ble_device Found a LE device: c0:53:c1:67:0e:a8

01-02 14:20:35.312128  2146  2634 D BtGatt.GattService: onScanResult() - eventType=0x1b, addressType=1, address=C0:53:C1:67:0E:A8, primaryPhy=1, secondaryPhy=0, advertisingSid=0xff, txPower=127, rssi=-82, periodicAdvInt=0x0, originalAddress=00:00:00:00:00:00


01-02 14:20:35.312390  2146  2634 D BluetoothRemoteDevices: Added new device property
01-02 14:20:35.312479  2146  2634 I BluetoothRemoteDevices: Property type: 1
01-02 14:20:35.313982  2146  2634 I BluetoothRemoteDevices: Remote Device name is: LE_WF-C500
01-02 14:20:35.314013  2146  2634 I BluetoothRemoteDevices: Property type: 4
01-02 14:20:35.314035  2146  2634 I BluetoothRemoteDevices: Skip class update for C0:53:C1:67:0E:A8
01-02 14:20:35.314046  2146  2634 I BluetoothRemoteDevices: Property type: 5

158	2025-01-02 14:20:35.308074	controller	host	HCI_EVT	41	Rcvd LE Meta (LE Extended Advertising Report)

Frame 158: 41 bytes on wire (328 bits), 41 bytes captured (328 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI Event - LE Meta
    Event Code: LE Meta (0x3e)
    Parameter Total Length: 38
    Sub Event: LE Extended Advertising Report (0x0d)
    Num Reports: 1
    Event Type: 0x0013, Connectable, Scannable, Legacy, Data Status: Complete
    Peer Address Type: Random Device Address (0x01)
    BD_ADDR: c0:53:c1:67:0e:a8 (c0:53:c1:67:0e:a8)
    Primary PHY: LE 1M (0x01)
    Secondary PHY: No packets on the secondary advertising channel (0x00)
    Advertising SID: 0xff (not available)
    TX Power: 127 dBm (not available)
    RSSI: -81 dBm
    Periodic Advertising Interval: 0x0000 (no periodic advertising)
    Direct Address Type: Public Device Address (0x00)
    Direct BD_ADDR: 00:00:00_00:00:00 (00:00:00:00:00:00)
    Data Length: 12
    Advertising Data
        Device Name: LE_WF-C500  // 设备名字


3. 例子三
01-02 14:20:41.776270  2146  2634 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:637 btif_update_remote_properties: cod from storage is also unclassified

01-02 14:20:41.776344  2146  2634 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btif_in_fetch_bonded_ble_device Found a LE device: d4:f0:ea:91:af:c6

01-02 14:20:41.776511  2146  2634 D BtGatt.GattService: onScanResult() - eventType=0x1b, addressType=0, address=D4:F0:EA:91:AF:C6, primaryPhy=1, secondaryPhy=0, advertisingSid=0xff, txPower=127, rssi=-84, periodicAdvInt=0x0, originalAddress=00:00:00:00:00:00


01-02 14:20:41.776644  2146  2634 D BluetoothRemoteDevices: Added new device property
01-02 14:20:41.776740  2146  2634 I BluetoothRemoteDevices: Property type: 1
01-02 14:20:41.777297  2146  2634 I BluetoothRemoteDevices: Remote Device name is: SMI-M14
01-02 14:20:41.777325  2146  2634 I BluetoothRemoteDevices: Property type: 4
01-02 14:20:41.777346  2146  2634 I BluetoothRemoteDevices: Skip class update for D4:F0:EA:91:AF:C6
01-02 14:20:41.777359  2146  2634 I BluetoothRemoteDevices: Property type: 5
735	2025-01-02 14:20:41.775853	controller	host	HCI_EVT	59	Rcvd LE Meta (LE Extended Advertising Report)

Frame 735: 59 bytes on wire (472 bits), 59 bytes captured (472 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI Event - LE Meta
    Event Code: LE Meta (0x3e)
    Parameter Total Length: 56
    Sub Event: LE Extended Advertising Report (0x0d)
    Num Reports: 1
    Event Type: 0x0013, Connectable, Scannable, Legacy, Data Status: Complete
    Peer Address Type: Public Device Address (0x00)
    BD_ADDR: BeijingX_91:af:c6 (d4:f0:ea:91:af:c6)
    Primary PHY: LE 1M (0x01)
    Secondary PHY: No packets on the secondary advertising channel (0x00)
    Advertising SID: 0xff (not available)
    TX Power: 127 dBm (not available)
    RSSI: -84 dBm
    Periodic Advertising Interval: 0x0000 (no periodic advertising)
    Direct Address Type: Public Device Address (0x00)
    Direct BD_ADDR: 00:00:00_00:00:00 (00:00:00:00:00:00)
    Data Length: 30
    Advertising Data
        Flags
            Length: 2
            Type: Flags (0x01)
            0. .... = Reserved: 0x0
            ...0 .... = Simultaneous LE and BR/EDR to Same Device Capable (Host): false (0x0)
            .... 0... = Simultaneous LE and BR/EDR to Same Device Capable (Controller): false (0x0)
            .... .1.. = BR/EDR Not Supported: true (0x1)
            .... ..1. = LE General Discoverable Mode: true (0x1)
            .... ...0 = LE Limited Discoverable Mode: false (0x0)
        Service Data - 16 bit UUID
            Length: 17
            Type: Service Data - 16 bit UUID (0x16)
            UUID 16: Xiaomi Inc. (0xfe95)
            Service Data: b054452d00c6af91eaf0d4080e00
        Device Name: SMI-M14  // 设备名字
            Length: 8
            Type: Device Name (0x09)
            Device Name: SMI-M14


2. SDP / 服务发现阶段

1.作用

  • 在 classic 蓝牙中,通过 SDP 获取远程设备支持的 UUID

  • 在 BLE 中,通过 GATT Discover Services 获取 UUID

  • 均更新到 DeviceProperties::uuids 字段中

2.解决的问题

问题 如何解决
重复做 SDP,浪费时间 已知 UUID 可直接进入连接逻辑
Profile 初始化失败 使用缓存 UUID 预判断是否支持 A2DP、HFP 等
连接效率低 提前缓存服务能力,减少阻塞等待时间

3.代码流程鉴赏

1. btif_dm_search_services_evt
1. BTA_DM_DISC_RES_EVT

这段代码是 Android AOSP 中蓝牙设备 服务发现结果事件(BTA_DM_DISC_RES_EVT)的处理逻辑,位于 btif_dm_search_services_evt 函数中。它主要完成 SDP 服务发现结果的处理、UUID 合并、A2DP 能力识别、属性上报,以及处理 SDP 失败的情况。

大体流程概览:

  1. 提取事件数据(设备地址、UUID 等)
  2. 判断是否是配对过程的设备
  3. 判断 SDP 是否失败并重试(带次数限制)
  4. 如果成功则合并 UUID、识别 A2DP 功能
  5. 特殊处理 LE Audio 设备
  6. 将 UUID 结果保存、上报到 Java 层
  7. 处理 SDP 失败时尝试使用 EIR 中的 UUID
  8. 最终调用回调通知 UUID 属性变化

/*******************************************************************************
 *
 * Function         btif_dm_search_services_evt
 *
 * Description      Executes search services event in btif context
 *
 * Returns          void
 *
 ******************************************************************************/
static void btif_dm_search_services_evt(tBTA_DM_SEARCH_EVT event,
                                        tBTA_DM_SEARCH* p_data) {
  switch (event) {
    case BTA_DM_DISC_RES_EVT: {
      // 初始化变量,用于保存服务发现结果、UUID 集合和属性值,a2dp_sink_capable 标记是否支持 A2DP Sink。
      bt_property_t prop;
      uint32_t i = 0;
      bt_status_t ret;
      std::vector<uint8_t> property_value;
      std::set<Uuid> uuids;
      bool a2dp_sink_capable = false;

	  // 获取服务发现设备的地址引用。
      RawAddress& bd_addr = p_data->disc_res.bd_addr;

	  // 判断当前 SDP 结果是否是为当前正在配对的设备服务。
      bool results_for_bonding_device =
        (bd_addr == pairing_cb.bd_addr || bd_addr == pairing_cb.static_bdaddr);

      // 日志输出 SDP 的返回值和服务字段。
      LOG_VERBOSE("result=0x%x, services 0x%x", p_data->disc_res.result,
                  p_data->disc_res.services);


      /*
	      SDP 失败后的重试逻辑(仅限配对设备):
      
      
      */
      if (results_for_bonding_device && p_data->disc_res.result != BTA_SUCCESS &&
          pairing_cb.state == BT_BOND_STATE_BONDED &&
          pairing_cb.sdp_attempts < BTIF_DM_MAX_SDP_ATTEMPTS_AFTER_PAIRING) { // 如果是配对设备、SDP 失败、当前状态为已配对、重试次数不超过限制:

		// 第一次不重试,第二次以上继续尝试,最多三次。
        if (pairing_cb.sdp_attempts) {
          LOG_WARN("SDP failed after bonding re-attempting for %s",
                   PRIVATE_ADDRESS(bd_addr));
          pairing_cb.sdp_attempts++;
          LOG_INFO("sdp_attempts = %d", pairing_cb.sdp_attempts);
          btif_dm_get_remote_services(bd_addr, BT_TRANSPORT_AUTO);
        } else {
          LOG_WARN("SDP triggered by someone failed when bonding");
        }
        return;
      }

	  // 如果是配对设备,则标记经典 SDP 已完成。
      if (results_for_bonding_device) {
        LOG_INFO("SDP finished for %s:", PRIVATE_ADDRESS(bd_addr));
        pairing_cb.sdp_over_classic =
            btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED;
      }

     // 设置 UUID 属性类型。
      prop.type = BT_PROPERTY_UUIDS;
      prop.len = 0;
      if ((p_data->disc_res.result == BTA_SUCCESS) &&
          (p_data->disc_res.num_uuids > 0)) { // 如果 SDP 成功且发现了 UUID:
          
        LOG_INFO("New UUIDs for %s:", bd_addr.ToString().c_str());
        for (i = 0; i < p_data->disc_res.num_uuids; i++) { // 遍历所有 UUID,过滤无效项,并插入到集合中。
          auto uuid = p_data->disc_res.p_uuid_list + i;
          if (btif_should_ignore_uuid(*uuid)) {
            continue;
          }
          LOG_INFO("index:%d uuid:%s", i, uuid->ToString().c_str());
          uuids.insert(*uuid);
        }
        // 将已有的 UUID 合并进去,避免覆盖。
        if (results_for_bonding_device) {
          btif_merge_existing_uuids(pairing_cb.static_bdaddr, &uuids);
          btif_merge_existing_uuids(pairing_cb.bd_addr, &uuids);
        } else {
          btif_merge_existing_uuids(bd_addr, &uuids);
        }
        // 将 UUID 转为 128bit 字节流,并识别 A2DP Sink 能力。
        for (auto& uuid : uuids) {
          auto uuid_128bit = uuid.To128BitBE();
          property_value.insert(property_value.end(), uuid_128bit.begin(),
                                uuid_128bit.end());
          if (uuid == UUID_A2DP_SINK) {
            a2dp_sink_capable = true;
          }
        }
        prop.val = (void*)property_value.data();
        prop.len = Uuid::kNumBytes128 * uuids.size();
      }


      // 如果同时支持 LE Audio 且 GATT 服务发现未完成,则推迟向 Java 层报告 UUID,等待 LE GATT 结果
      bool skip_reporting_wait_for_le = false;
      /* If we are doing service discovery for device that just bonded, that is
       * capable of a2dp, and both sides can do LE Audio, and it haven't
       * finished GATT over LE yet, then wait for LE service discovery to finish
       * before before passing services to upper layers. */
      if (results_for_bonding_device &&
          a2dp_sink_capable && LeAudioClient::IsLeAudioClientRunning() &&
          pairing_cb.gatt_over_le !=
              btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED &&
          (check_cod_le_audio(bd_addr) ||
           metadata_cb.le_audio_cache.contains(bd_addr) ||
           BTA_DmCheckLeAudioCapable(bd_addr))) {
        skip_reporting_wait_for_le = true;
      }

      /* onUuidChanged requires getBondedDevices to be populated.
      ** bond_state_changed needs to be sent prior to remote_device_property
      */
      auto num_eir_uuids = 0;
      Uuid uuid = {};
      // 只在 SDP + 配对都完成后进入。
      if (pairing_cb.state == BT_BOND_STATE_BONDED && pairing_cb.sdp_attempts &&
          results_for_bonding_device) {
        LOG_INFO("SDP search done for %s", bd_addr.ToString().c_str());
        pairing_cb.sdp_attempts = 0; // 重置重试计数。
        LOG_INFO("sdp_attempts = %d", pairing_cb.sdp_attempts);

        // Send UUIDs discovered through EIR to Java to unblock pairing intent
        // when SDP failed or no UUID is discovered
        if (p_data->disc_res.result != BTA_SUCCESS ||
            p_data->disc_res.num_uuids == 0) { // 如果 SDP 失败或没有发现 UUID:
          auto uuids_iter = eir_uuids_cache.find(bd_addr);
          if (uuids_iter != eir_uuids_cache.end()) { // 尝试从缓存中读取 EIR UUID 并上报。
            num_eir_uuids = static_cast<int>(uuids_iter->second.size());
            LOG_INFO("SDP failed, send %d EIR UUIDs to unblock bonding %s",
                     num_eir_uuids, bd_addr.ToString().c_str());
            for (auto eir_uuid : uuids_iter->second) {
              auto uuid_128bit = eir_uuid.To128BitBE();
              property_value.insert(property_value.end(), uuid_128bit.begin(),
                                    uuid_128bit.end());
            }
            eir_uuids_cache.erase(uuids_iter);
          }
          if (num_eir_uuids > 0) {
            prop.val = (void*)property_value.data();
            prop.len = num_eir_uuids * Uuid::kNumBytes128;
          } else { // 若 EIR 无内容,则构造空 UUID。
            LOG_WARN("SDP failed and we have no EIR UUIDs to report either");
            prop.val = &uuid;
            prop.len = Uuid::kNumBytes128;
          }
        }
        // Both SDP and bonding are done, clear pairing control block in case
        // it is not already cleared
        pairing_cb = {}; // 清除 pairing_cb 控制块。
        LOG_INFO("clearing btif pairing_cb");
      }

      // 将 UUID 存储到本地数据库。
      if (p_data->disc_res.num_uuids != 0 || num_eir_uuids != 0) {
        /* Also write this to the NVRAM */
        ret = btif_storage_set_remote_device_property(&bd_addr, &prop);
        ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed",
                ret);

        if (skip_reporting_wait_for_le) { // 如果不是 LE Audio 的延迟上报情况,则立即通过回调通知 Java 层属性已更新。
          LOG_INFO(
              "Bonding LE Audio sink - must wait for le services discovery "
              "to pass all services to java %s",
              PRIVATE_ADDRESS(bd_addr));
          /* For LE Audio capable devices, we care more about passing GATT LE
           * services than about just finishing pairing. Service discovery
           * should be scheduled when LE pairing finishes, by call to
           * btif_dm_get_remote_services(bd_addr, BT_TRANSPORT_LE) */
          return;
        }

        /* Send the event to the BTIF */
        invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr, 1,
                                           &prop);
      }
    } break;
	...
  }
功能点 实现逻辑
是否是配对设备 比较地址匹配 pairing_cb
SDP 重试 最多重试 3 次,间隔触发
UUID 处理 收集、过滤、合并、识别 A2DP 能力
LE Audio 若需 GATT 完成则跳过上报
SDP 失败回退 通过 EIR UUID 解锁配对流程
属性更新 保存到数据库 + 上报 Java 层


01-02 14:21:00.149273  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1638 btif_dm_search_services_evt: SDP finished for xx:xx:xx:xx:b0:62:
01-02 14:21:00.149282  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1647 btif_dm_search_services_evt: New UUIDs for 70:8f:47:91:b0:62:
01-02 14:21:00.149295  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:0 uuid:0000110a-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149309  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:1 uuid:00001105-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149320  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:2 uuid:00001115-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149332  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:3 uuid:00001116-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149343  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:4 uuid:0000112d-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149355  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:5 uuid:0000110e-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149366  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:6 uuid:0000112f-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149378  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:7 uuid:00001112-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149397  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:8 uuid:0000111f-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149409  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:9 uuid:00001132-0000-1000-8000-00805f9b34fb
01-02 14:21:00.149423  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:12 uuid:8fa9c715-bd1f-596c-a1b0-13162b15c892
01-02 14:21:00.149441  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:13 uuid:2c042b0a-7f57-4c0a-afcf-1762af70257c
01-02 14:21:00.149453  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1653 btif_dm_search_services_evt: index:14 uuid:9fed64fd-e91a-499e-88dd-73dfe023feed
01-02 14:21:00.149859  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1696 btif_dm_search_services_evt: SDP search done for 70:8f:47:91:b0:62
01-02 14:21:00.149882  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1698 btif_dm_search_services_evt: sdp_attempts = 0
01-02 14:21:00.149891  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1728 btif_dm_search_services_evt: clearing btif pairing_cb
  • 车机向 手机请求 sdp
976	2025-01-02 14:21:00.107670	f8:6b:14:d1:ec:32 (cheji)	vivoMobi_91:b0:62 (cbx)	SDP	31	Sent Service Search Attribute Request : L2CAP: Attribute Range (0x0000 - 0xffff) 

Frame 976: 31 bytes on wire (248 bits), 31 bytes captured (248 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Bluetooth SDP Protocol
    PDU: Service Search Attribute Request (0x06)
    Transaction Id: 0x0001
    Parameter Length: 17
    Service Search Pattern: L2CAP
    Maximum Attribute Byte Count: 1008
    Attribute ID List
    Continuation State: yes (03 F0)

  • 手机向 车机返回结果:
980	2025-01-02 14:21:00.117463	vivoMobi_91:b0:62 (cbx)	f8:6b:14:d1:ec:32 (Mycar28825)	SDP	487	Rcvd Service Search Attribute Response 

Frame 980: 487 bytes on wire (3896 bits), 487 bytes captured (3896 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI ACL Packet
Bluetooth L2CAP Protocol
Bluetooth SDP Protocol
    PDU: Service Search Attribute Response (0x07)
    Transaction Id: 0x0001
    Parameter Length: 473
    Attribute List Byte Count: 470
    Data Fragment
    Continuation State: no (00)
    [Reassembled Attribute List]
        Attribute Lists [count = 16]
            Data Element: Sequence uint16 1475 bytes
                0011 0... = Data Element Type: Sequence (6)
                .... .110 = Data Element Size: uint16 (6)
                Data Element Var Size: 1475
                Data Value
                    Attribute List [count =  4] (Generic Attribute Profile)
                    Attribute List [count =  4] (Generic Access Profile)
                    Attribute List [count =  6] (Headset Audio Gateway)
                    Attribute List [count =  8] (Handsfree Audio Gateway)
                    Attribute List [count =  8] (A/V Remote Control Target)
                    Attribute List [count =  7] (Audio Source)
                    Attribute List [count =  7] (A/V Remote Control)
                    Attribute List [count = 11] (PAN NAP)
                    Attribute List [count =  9] (PAN PANU)
                    Attribute List [count =  6] (SIM Access)
                    Attribute List [count =  7] (Phonebook Access Server)
                    Attribute List [count = 10] (Message Access Server)
                    Attribute List [count =  8] (OBEX Object Push)
                    Attribute List [count =  5] (CustomUUID: Unknown)
                    Attribute List [count =  4] (CustomUUID: Unknown)
                    Attribute List [count =  4] (CustomUUID: Unknown)

2. BTA_DM_GATT_OVER_LE_RES_EVT

这段代码,它处理的是 通过 GATT over LE 发现到的服务 UUID,这是在蓝牙设备进行 LE Audio 相关配对和服务发现时的关键步骤之一。

static void btif_dm_search_services_evt(tBTA_DM_SEARCH_EVT event,
                                        tBTA_DM_SEARCH* p_data) {
  switch (event) {

	// 表示收到 BLE GATT 服务发现完成事件,主要处理通过 GATT over LE 获取到的服务信息。
    case BTA_DM_GATT_OVER_LE_RES_EVT: {
		/*
			num_properties: 记录即将设置的属性数量(如 UUID、名称)。
			
			prop: 保存即将传递给上层的属性,最多两个(UUIDs + 名称)。
			
			property_value: 保存所有服务 UUID 的序列化值。
			
			uuids: 使用 set 记录 UUID,防止重复。
			
			bd_addr: 当前发现服务的设备地址。
		*/
      int num_properties = 0;
      bt_property_t prop[2];
      std::vector<uint8_t> property_value;
      std::set<Uuid> uuids;
      RawAddress& bd_addr = p_data->disc_ble_res.bd_addr;

      if (event == BTA_DM_GATT_OVER_LE_RES_EVT) {
        LOG_INFO("New GATT over LE UUIDs for %s:",
                 PRIVATE_ADDRESS(bd_addr));
        if ((bd_addr == pairing_cb.bd_addr ||
             bd_addr == pairing_cb.static_bdaddr)) {
          if (pairing_cb.gatt_over_le !=
              btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED) {
            LOG_ERROR(
                "gatt_over_le should be SCHEDULED, did someone clear the "
                "control block for %s ?",
                PRIVATE_ADDRESS(bd_addr));
          }
          /*
			    如果当前设备是正在配对的目标设备,更新配对控制块 pairing_cb 的 GATT 状态为已完成。
	
				同时判断 gatt_over_le 状态是否应该是 SCHEDULED(即之前已经调度过该设备的服务发现)——否则可能说明状态机错乱。
          
          */
          pairing_cb.gatt_over_le =
              btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED;

          if (pairing_cb.sdp_over_classic !=
              btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED) {
              // 如果 classic SDP 服务发现也没有调度或已经完成,说明整个配对与服务发现流程都已完成,可以安全清除 pairing_cb。
            // Both SDP and bonding are either done, or not scheduled,
            // we are safe to clear the service discovery part of CB.
            LOG_INFO("clearing pairing_cb");
            pairing_cb = {};
          }
        }
      } else {
        // 这段用于处理 BTA_DM_GATT_OVER_SDP_RES_EVT(即通过 classic SDP 获取 GATT UUID),但这里实际处理的是 GATT_OVER_LE_RES_EVT。
        LOG_INFO("New GATT over SDP UUIDs for %s:", PRIVATE_ADDRESS(bd_addr));
      }

      for (Uuid uuid : *p_data->disc_ble_res.services) {
        /*
		    遍历发现到的 GATT 服务 UUID:
				
				只保留 “有趣的” LE 服务(如 Hearing Aid、LE Audio)。
				
				忽略指定的 UUID。
				
				使用 set 自动去重。
        */
        if (btif_is_interesting_le_service(uuid)) {
          if (btif_should_ignore_uuid(uuid)) {
            continue;
          }
          LOG_INFO("index:%d uuid:%s", static_cast<int>(uuids.size()),
                   uuid.ToString().c_str());
          uuids.insert(uuid);
        }
      }

      if (uuids.empty()) {
        // 如果没有任何服务 UUID 被记录,直接退出。
        LOG_INFO("No well known GATT services discovered");
        return;
      }

	  // 读取该设备之前已经缓存的 UUID 并合并,防止遗漏或丢失信息。
      Uuid existing_uuids[BT_MAX_NUM_UUIDS] = {};
      btif_get_existing_uuids(&bd_addr, existing_uuids);
      for (int i = 0; i < BT_MAX_NUM_UUIDS; i++) {
        Uuid uuid = existing_uuids[i];
        if (uuid.IsEmpty()) {
          continue;
        }
        uuids.insert(uuid);
      }

		// 将 UUID 以 128bit Big-Endian 格式写入 property_value,用于上层 Java 层接收。
      for (auto& uuid : uuids) {
        auto uuid_128bit = uuid.To128BitBE();
        property_value.insert(property_value.end(), uuid_128bit.begin(),
                              uuid_128bit.end());
      }

	  // 设置 UUID 属性并保存到 内存中
      prop[0].type = BT_PROPERTY_UUIDS;
      prop[0].val = (void*)property_value.data();
      prop[0].len = Uuid::kNumBytes128 * uuids.size();

      /* Also write this to the NVRAM */
      bt_status_t ret =
          btif_storage_set_remote_device_property(&bd_addr, &prop[0]);
      ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret);
      num_properties++;

      // 如果通过 BLE GATT 回包中包含设备名,就保存远程设备名称属性。
      /* Remote name update */
      if (strnlen((const char*)p_data->disc_ble_res.bd_name, BD_NAME_LEN)) {
        prop[1].type = BT_PROPERTY_BDNAME;
        prop[1].val = p_data->disc_ble_res.bd_name;
        prop[1].len = strnlen((char*)p_data->disc_ble_res.bd_name, BD_NAME_LEN);

        ret = btif_storage_set_remote_device_property(&bd_addr, &prop[1]);
        ASSERTC(ret == BT_STATUS_SUCCESS,
                "failed to save remote device property", ret);
        num_properties++;
      }

      // 如果当前是 GATT over SDP 的结果,不在这里直接上报,而是等待 DISC_RES_EVT 一并处理
      /* If services were returned as part of SDP discovery, we will immediately
       * send them with rest of SDP results in BTA_DM_DISC_RES_EVT */
      if (event == BTA_DM_GATT_OVER_SDP_RES_EVT) {
        return;
      }


		// 将 UUID 和名称(如果有)回调通知上层应用,例如用于 Java API BluetoothDevice.getUuids() 或用于触发服务绑定。
      /* Send the event to the BTIF */
      invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr,
                                         num_properties, prop);
    } break;

    default: { ASSERTC(0, "unhandled search services event", event); } break;
  }

此 case 的主要作用是处理通过 BLE GATT 通道 发现的服务 UUID,具体流程如下:

步骤 操作
1 判断是否是当前配对目标
2 标记 GATT 服务发现已完成
3 从 GATT 发现包中提取 UUID,并过滤无关的
4 合并之前已缓存的 UUID(避免丢失)
5 保存 UUID 属性并更新 NVRAM
6 如有远程设备名,也一并保存
7 最后回调通知 Java 层,完成 BLE 服务发现过程


3. 配对阶段(Pairing / Bonding)

1. 作用

  • 缓存配对结果:Bond 状态、IO 能力、安全连接支持与否、LinkKey 类型与值

  • Profile 模块可依赖 DeviceProperties 判断是否可信 / 是否可加密传输

2. 写入 bt_config.conf 内容

字段 示例
Bonded true
LinkKey base64 编码
LinkKeyType 5(Unauthenticated Combination)
IOCapability 3(No Input No Output)
SecureConnection true

3. 解决的问题

问题 如何解决
重启系统后丢失配对信息 LinkKey / Bond 信息持久化
多设备状态混乱 每个设备独立记录属性
不确定是否加密传输 使用缓存 IO 能力与 LinkKey 属性判断安全等级

4. 代码鉴赏

1. btif_dm_ssp_cfm_req_evt

btif_dm_ssp_cfm_req_evt 函数

  • 它在 Bluetooth Secure Simple Pairing(SSP)确认请求事件 到达时执行。

这个函数在收到远端设备发起的 SSP 请求(特别是 Just Works 或 Passkey Confirmation 模式)时:

  • 判断是否可继续配对;

  • 设置配对状态;

  • 依据配对模式判断是否自动接受;

  • 最终将请求上报给 Java 层显示 UI 以确认(如弹窗显示 PIN)。

// system/btif/src/btif_dm.cc

/*******************************************************************************
 *
 * Function         btif_dm_ssp_cfm_req_evt
 *
 * Description      Executes SSP confirm request event in btif context
 *
 * Returns          void
 *
 ******************************************************************************/
// 接收一个指向 SSP 确认请求的结构体指针 p_ssp_cfm_req,包含发起配对的设备地址、名称、class、配对模式(Just Works / Passkey)、数字比较数值等信息
static void btif_dm_ssp_cfm_req_evt(tBTA_DM_SP_CFM_REQ* p_ssp_cfm_req) {
  bt_bdname_t bd_name;
  bool is_incoming = !(pairing_cb.state == BT_BOND_STATE_BONDING); // 判断当前是否是被动配对(未处于 BONDING 状态)。
  uint32_t cod; // Class of Device(设备类型编码)。
  int dev_type; //  设备类型(BR/EDR、BLE 或 Dual Mode)。

  BTIF_TRACE_DEBUG("%s", __func__);


  /*
	优先通过 feature 判断远端是否为 dual-mode。
	
	若失败,则尝试查询设备类型。
	
	查询失败,默认视为传统 BR/EDR 设备。
  */
  /* Remote properties update */
  if (BTM_GetPeerDeviceTypeFromFeatures(p_ssp_cfm_req->bd_addr) ==
      BT_DEVICE_TYPE_DUMO) {
    dev_type = BT_DEVICE_TYPE_DUMO;
  } else if (!btif_get_device_type(p_ssp_cfm_req->bd_addr, &dev_type)) {
    // Failed to get device type, defaulting to BR/EDR.
    dev_type = BT_DEVICE_TYPE_BREDR;
  }


  // 更新设备的属性缓存:名称、Class of Device 和类型。
  btif_update_remote_properties(p_ssp_cfm_req->bd_addr, p_ssp_cfm_req->bd_name,
                                p_ssp_cfm_req->dev_class,
                                (tBT_DEVICE_TYPE)dev_type);

  // 缓存设备地址和名称,后面用于显示和判断。
  RawAddress bd_addr = p_ssp_cfm_req->bd_addr;
  memcpy(bd_name.name, p_ssp_cfm_req->bd_name, BD_NAME_LEN);

  // 如果当前正在和某个设备配对,而新来的请求来自另一个设备,说明存在配对冲突。
  if (pairing_cb.state == BT_BOND_STATE_BONDING &&
      bd_addr != pairing_cb.bd_addr) {
    BTIF_TRACE_WARNING("%s(): already in bonding state, reject request",
                       __FUNCTION__);
    // 拒绝该请求,防止状态混乱或被攻击。
    btif_dm_ssp_reply(bd_addr, BT_SSP_VARIANT_PASSKEY_CONFIRMATION, 0);
    return;
  }

  /* Set the pairing_cb based on the local & remote authentication requirements
   */
  // 将当前设备设置为 BONDING 状态,并上报配对事件(Java 层通过广播感知)。
  bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);

  // 记录当前配对是否为 Just Works,以及双方的认证需求。
  BTIF_TRACE_EVENT("%s: just_works:%d, loc_auth_req=%d, rmt_auth_req=%d",
                   __func__, p_ssp_cfm_req->just_works,
                   p_ssp_cfm_req->loc_auth_req, p_ssp_cfm_req->rmt_auth_req);

	/*
		判断是否是临时配对
		
			如果是 Just Works 模式,且:
			
				双方都没有要求配对记忆(bonding)
				
				也不是鼠标等 pointing device
	
	*/
  /* if just_works and bonding bit is not set treat this as temporary */
  if (p_ssp_cfm_req->just_works &&
      !(p_ssp_cfm_req->loc_auth_req & BTM_AUTH_BONDS) &&
      !(p_ssp_cfm_req->rmt_auth_req & BTM_AUTH_BONDS) &&
      !(check_cod((RawAddress*)&p_ssp_cfm_req->bd_addr, COD_HID_POINTING)))
    pairing_cb.bond_type = tBTM_SEC_DEV_REC::BOND_TYPE_TEMPORARY; // 那么是“临时配对”,
  else
    pairing_cb.bond_type = tBTM_SEC_DEV_REC::BOND_TYPE_PERSISTENT;  // 否则就是“持久配对”(会保存)。

  btm_set_bond_type_dev(p_ssp_cfm_req->bd_addr, pairing_cb.bond_type); // 将配对类型同步设置到底层。

  pairing_cb.is_ssp = true; // 设置 pairing_cb 中当前为 SSP 配对(而不是 legacy pairing)。

  /* If JustWorks auto-accept */
  if (p_ssp_cfm_req->just_works) { // 自动接受 Just Works 模式的特殊情况
    /* Pairing consent for JustWorks NOT needed if:
     * 1. Incoming temporary pairing is detected
     */
    if (is_incoming &&
        pairing_cb.bond_type == tBTM_SEC_DEV_REC::BOND_TYPE_TEMPORARY) {
        /*
	        如果是 Just Works 模式,
	
				且当前是被动发起(远端请求配对)、
				
				且是临时配对:
        */
      BTIF_TRACE_EVENT(
          "%s: Auto-accept JustWorks pairing for temporary incoming", __func__);
      btif_dm_ssp_reply(bd_addr, BT_SSP_VARIANT_CONSENT, true); // 自动接受该配对,无需用户确认。这用于临时设备,比如游戏手柄、耳机等
      return;
    }
  }

  cod = devclass2uint(p_ssp_cfm_req->dev_class); // 将设备 class(3 字节)转为整型数值。

  if (cod == 0) { // 如果为 0(未知),设置为未分类。
    LOG_INFO("%s cod is 0, set as unclassified", __func__);
    cod = COD_UNCLASSIFIED;
  }

  pairing_cb.sdp_attempts = 0; // 刚进入配对流程,SDP 尝试次数设为 0。
  LOG_INFO("sdp_attempts = %d", pairing_cb.sdp_attempts);

	/*
		通知 Java 层显示配对确认弹窗
		
			调用回调函数,通知 Java 层出现确认界面。
			
			BT_SSP_VARIANT_CONSENT → Just Works 模式(只需确认,不输入)。
			
			BT_SSP_VARIANT_PASSKEY_CONFIRMATION → 数字比较模式(确认6位数字相同)。
			
			num_val 就是要比较的那6位数字。
	*/
  invoke_ssp_request_cb(
      bd_addr, bd_name, cod,
      (p_ssp_cfm_req->just_works ? BT_SSP_VARIANT_CONSENT
                                 : BT_SSP_VARIANT_PASSKEY_CONFIRMATION),
      p_ssp_cfm_req->num_val);
}
步骤 动作
1 判断是否为当前正在配对的设备,若不是则拒绝
2 更新设备类型、属性
3 设置 pairing_cb 结构体状态
4 如果是 JustWorks 且临时设备、被动连接,直接自动接受
5 如果需要用户确认(JustWorks 或 Passkey Compare),则通知 Java 层 UI 确认
6 设置配对类型、认证信息等上下文,准备后续配对流程

01-02 14:20:56.052840  2146  3220 I bt_btm  : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: BTM_SP_CFM_REQ_EVT:  num_val: 363625
01-02 14:20:56.052917  2146  3220 I bt_btm  : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btm_proc_sp_req_evt()  just_works:0, io loc:1, rmt:1, auth loc:3, rmt:3
01-02 14:20:56.052933  2146  3220 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: bta_dm_sp_cback: 2
01-02 14:20:56.052948  2146  3220 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: btif_dm_upstreams_evt: ev: BTA_DM_SP_CFM_REQ_EVT
01-02 14:20:56.052958  2146  3220 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btif_dm_ssp_cfm_req_evt
01-02 14:20:56.052999  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:641 btif_update_remote_properties: class of device (cod) is 0x5a020c
01-02 14:20:56.053026  2146  3220 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btif_in_fetch_bonded_ble_device Found a LE device: 70:8f:47:91:b0:62
01-02 14:20:56.053116  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:462 get_cod: get_cod remote_cod = 0x005a020c
01-02 14:20:56.053294  2146  2634 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: operator(): HAL bt_hal_cbacks->remote_device_properties_cb
01-02 14:20:56.053394  2146  3220 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: btif_dm_ssp_cfm_req_evt: just_works:0, loc_auth_req=3, rmt_auth_req=3
01-02 14:20:56.053424  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:974 btif_dm_ssp_cfm_req_evt: sdp_attempts = 0
01-02 14:20:56.053441  2146  3220 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: dm status: 1
01-02 14:20:56.053461  2146  2634 I BluetoothRemoteDevices: Property type: 1
01-02 14:20:56.053486  2146  2634 I BluetoothRemoteDevices: Skip name update for 70:8F:47:91:B0:62
01-02 14:20:56.053525  2146  2634 I BluetoothRemoteDevices: Property type: 4
01-02 14:20:56.053559  2146  2634 I BluetoothRemoteDevices: Skip class update for 70:8F:47:91:B0:62
01-02 14:20:56.053570  2146  2634 I BluetoothRemoteDevices: Property type: 5
01-02 14:20:56.053601  2146  2634 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: operator(): HAL bt_hal_cbacks->ssp_request_cb
01-02 14:20:56.053739  2146  2634 I BluetoothBondStateMachine: sspRequestCallback: [B@c3ee023 name: [B@d224920 cod: 5898764 pairingVariant 0 passkey: 363625
01-02 14:20:56.055029  2146  2719 D BluetoothBondStateMachine: PendingCommandState: processMessage msg.what=5 dev=70:8F:47:91:B0:62 (cbx)

4. 连接阶段(Connection)

1. 作用

  • 在连接发起前,查询:

    • 是否已配对

    • 是否支持某 UUID

    • 是否要求安全连接

  • 用于 Profile 判断:

    • 是否建立连接

    • 是否先 SDP

    • 是否强制加密通道

2. 解决的问题

问题 如何解决
每次连接都等待 SDP 完成 使用缓存 UUID 快速判断
Profile 初始化失败 利用缓存状态预检能力
重复连接失败 使用缓存 Key 与安全属性重用连接通道

3. 代码鉴赏

当 app 层调用 BluetoothDevice.connect 时,将会触发 bt.server 调用

// android/app/src/com/android/bluetooth/btservice/AdapterService.java

    private int connectEnabledProfiles(BluetoothDevice device) {
        ParcelUuid[] remoteDeviceUuids = getRemoteUuids(device); // 会先获取 该设备的 uuid 信息。 该信息是在 SDP 阶段 上报上来的。
        ParcelUuid[] localDeviceUuids = mAdapterProperties.getUuids();

        // 如果 远端和 本地 都支持 a2dp 那么就连接 a2dp
        if (mA2dpService != null && isSupported(localDeviceUuids, remoteDeviceUuids,
                BluetoothProfile.A2DP, device)) {
            Log.i(TAG, "connectEnabledProfiles: Connecting A2dp");
            mA2dpService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_AUTOCONNECT);
            mA2dpService.connect(device);
        }
		...
	}


    public ParcelUuid[] getRemoteUuids(BluetoothDevice device) {
        DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
        if (deviceProp == null) {
            return null;
        }
        return deviceProp.getUuids();
    }
// android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
        ParcelUuid[] getUuids() {
            synchronized (mObject) {
                return mUuids;
            }
        }


  • 下面是 sdp 完成后, 上报 uuid 的日志
01-02 14:21:00.149859  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1696 btif_dm_search_services_evt: SDP search done for 70:8f:47:91:b0:62
01-02 14:21:00.149882  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1698 btif_dm_search_services_evt: sdp_attempts = 0
01-02 14:21:00.149891  2146  3220 I bt_btif_dm: packages/modules/Bluetooth/system/btif/src/btif_dm.cc:1728 btif_dm_search_services_evt: clearing btif pairing_cb
01-02 14:21:00.150000  2146  3220 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:201 LogMsg: btif_in_fetch_bonded_ble_device Found a LE device: 70:8f:47:91:b0:62
01-02 14:21:00.153761   723   723 I V4L2CameraHAL: V4L2CameraHAL:V4L2CameraHAL:71: ais_v4l2_proxy not ready. try again.
01-02 14:21:00.159003  2146  3220 I bt_bta_dm: packages/modules/Bluetooth/system/bta/dm/bta_dm_act.cc:1371 bta_dm_search_cmpl: No BLE connection, processing classic results
01-02 14:21:00.159092  2146  2634 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: operator(): HAL bt_hal_cbacks->remote_device_properties_cb
01-02 14:21:00.159240  2146  2634 I BluetoothRemoteDevices: Property type: 3
01-02 14:21:00.159360  2146  2634 I BluetoothAdapterService: sendUuidsInternal: Received service discovery UUIDs for device 70:8F:47:91:B0:62
01-02 14:21:00.159403  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=0 uuid=00001105-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159436  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=1 uuid=0000110a-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159462  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=2 uuid=0000110c-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159493  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=3 uuid=0000110e-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159558  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=4 uuid=00001112-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159587  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=5 uuid=00001115-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159607  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=6 uuid=00001116-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159628  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=7 uuid=0000111f-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159694  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=8 uuid=0000112d-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159725  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=9 uuid=0000112f-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159746  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=10 uuid=00001132-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159766  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=11 uuid=00001200-0000-1000-8000-00805f9b34fb
01-02 14:21:00.159792  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=12 uuid=2c042b0a-7f57-4c0a-afcf-1762af70257c
01-02 14:21:00.159815  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=13 uuid=8fa9c715-bd1f-596c-a1b0-13162b15c892
01-02 14:21:00.159835  2146  2634 D BluetoothAdapterService: sendUuidsInternal: index=14 uuid=9fed64fd-e91a-499e-88dd-73dfe023feed
01-02 14:21:00.160431  2146  2719 D BluetoothBondStateMachine: StableState: processMessage msg.what=10 dev=70:8F:47:91:B0:62 (cbx)
01-02 14:21:00.161052  2048  2487 D BluetoothDevice: getUuids()
01-02 14:21:00.162895  2146  2719 I BluetoothBondStateMachine: Bond State Change Intent:70:8F:47:91:B0:62 (cbx) BOND_BONDING => BOND_BONDED

    void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
        Intent intent;
        byte[] val;
        int type;
        BluetoothDevice bdDevice = getDevice(address);
        DeviceProperties deviceProperties;
        if (bdDevice == null) {
            debugLog("Added new device property");
            deviceProperties = addDeviceProperties(address);
            bdDevice = getDevice(address);
        } else {
            deviceProperties = getDeviceProperties(bdDevice);
        }

        if (types.length <= 0) {
            errorLog("No properties to update");
            return;
        }

        for (int j = 0; j < types.length; j++) {
            type = types[j];
            val = values[j];
            if (val.length > 0) {
                synchronized (mObject) {
                    infoLog("Property type: " + type);
                    switch (type) {
	                    ...
                        case AbstractionLayer.BT_PROPERTY_UUIDS: // 0x03
                            int numUuids = val.length / AbstractionLayer.BT_UUID_SIZE;
                            final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val);
                            //ParcelUuid[] uuids = updateUuids(deviceProperties.mUuids, newUuids);
                            if (areUuidsEqual(newUuids, deviceProperties.mUuids)) {
                                infoLog( "Skip uuids update for " + bdDevice.getAddress());
                                break;
                            }
                            deviceProperties.mUuids = newUuids;
                            if (sAdapterService.getState() == BluetoothAdapter.STATE_ON) {
                                sAdapterService.deviceUuidUpdated(bdDevice);
                                sendUuidIntent(bdDevice, deviceProperties);
                            } else if (sAdapterService.getState()
                                    == BluetoothAdapter.STATE_BLE_ON) {
                                sAdapterService.deviceUuidUpdated(bdDevice);
                            }
                            break;
                            ...
                    }
                }
            }
        }
    }

5. 重点问题

1. 搜索(Discovery)阶段不会写入 bt_stack.conf 的原因

1. 目的:快速获取周围设备信息,不持久化

  • 搜索时获取到的是临时的广播信息(例如设备名称、地址、Class of Device、RSSI 等),系统主要用于UI 展示或后续配对流程判断。

  • 尚未配对(bond)或连接的设备,不需要写入配置文件,因为:

    • 这些信息随时可能变化;

    • 搜索结果可能包含大量设备,不宜持久化;

    • 节省写入频率,减少 I/O 消耗和 flash 损耗;

    • 避免保存不可信或无效设备。


2. 那什么时候才会写入 bt_config.confbt_stack.conf

设备信息只有在以下场景中,才会被持久化到 bt_config.conf(旧系统)或 bt_stack.conf(AOSP 13 后被 StorageModule 管理)中:

1. 配对成功(Bonding Success)
  • 成功配对后,系统将设备标记为“可信设备”(bonded),会记录:

    • 地址(MAC)

    • 设备名称

    • 连接方式(BR/EDR 或 LE)

    • link key / LE key(加密用)

    • profile 支持(如 A2DP、HID 等)

    • services UUIDs(通常来自 SDP)

2. SDP 完成后(部分 Profile 使用)
  • 若设备支持 classic profile,在配对后通常会进行 SDP 查询服务信息(如 A2DP、HFP)。

  • 得到的 UUID 信息、PSM、版本等,也会被写入配置中供下次连接使用。

3. 主动连接成功
  • 即使未配对,但连接了某些 BLE 服务,系统也可能缓存部分 GATT 服务信息用于快速重连,但不一定写入 config 文件。

2. StorageModule 与 config 文件的关系(AOSP 13+)

在 AOSP 13 中,DeviceProperties 模块已被 StorageModule 接管持久化工作:

  • StorageModule 是新的统一设备信息存取接口。

  • bt_config.conf 文件依然存在于 /data/misc/bluedroid/,但读写是通过 StorageModule 来完成的。

  • StorageModule 会在合适的时机(如配对成功、服务发现)将设备信息写入文件。


3. 总结重点

阶段 是否写入配置文件(bt_config.conf / bt_stack.conf) 说明
🔍 搜索阶段 ❌ 不写入 信息临时,仅用于展示
🤝 配对成功 ✅ 写入 标志为可信设备,保存 key 和 profile
📡 SDP 成功 ✅ 写入 保存服务 UUID 等
🔌 BLE 连接 可能写入(部分缓存) 依 profile 情况而定
📁 存储模块 StorageModule 统一管理配置 负责设备信息加载/保存

6. 总结

阶段 作用 是否写入配置 解决的问题
搜索 缓存名称/类型/RSSI,避免重复 设备显示混乱、信息缺失
SDP 缓存支持 UUID 提前判断服务能力
配对 缓存 bond/key、安全能力 配对信息持久化,安全连接判断
连接 提供连接前判断支持 否(使用缓存) 连接初始化优化,profile 可预判

DeviceProperties 是 AOSP 蓝牙系统中的“设备状态中台”,统一缓存、更新并与 StorageModule 配合写入持久化文件,确保设备从被发现 → 服务识别 → 配对 → 连接全过程中,系统始终拥有最新、稳定、可持续利用的设备状态数据



网站公告

今日签到

点亮在社区的每一天
去签到