Android BLE scan流程及源码分析

在 Android 系统中,startScan 方法用于启动蓝牙扫描,本文针对较新的 Android 版本14进行BLE扫描源码分析。

一、Android Ble scan的一般流程概述

1. 权限和蓝牙适配器检查

  • 应用需要确保具有适当的蓝牙和位置权限(从 Android 6.0 开始,蓝牙扫描通常需要位置权限)。
  • 应用还需要检查蓝牙适配器是否可用并启用。

2. 获取 BluetoothLeScanner

  • 通过 BluetoothAdapter 获取 BluetoothLeScanner 实例。这是启动 BLE 扫描的主要接口。

3. 配置扫描设置和过滤器(可选)

  • 使用 ScanSettings 配置扫描模式(如低延迟、平衡、低功耗)、回调类型等。
  • 使用 ScanFilter(如果有的话)来过滤扫描结果,只接收感兴趣的蓝牙设备信息。

4. 启动扫描

  • 调用 BluetoothLeScanner 的 startScan 方法,传入之前配置的 ScanSettings 和 ScanFilter(如果有的话),以及一个 ScanCallback 来接收扫描结果。

5. 扫描回调处理

  • 在 ScanCallback 的 onScanResult 方法中处理每个扫描到的设备信息。
  • 也可以处理批量扫描结果(如果启用了批量扫描模式)和扫描失败的情况。

6. 停止扫描

  • 当不再需要扫描时,调用 BluetoothLeScanner 的 stopScan 方法停止扫描。这是释放资源并避免不必要的电量消耗的重要步骤。

二、源码流程

在Android中,对于BLE(Bluetooth Low Energy)扫描,通常会使用BluetoothAdapter来获取BluetoothLeScanner的实例,然后调用BluetoothLeScannerstartScan方法来启动扫描过程。这个过程允许应用发现附近的BLE设备。

1. frameworks层:startScan

使用BluetoothLeScanner,指定ScanCallback进行扫描。

/packages/modules/Bluetooth/framework/java/android/bluetooth/le/BluetoothLeScanner.java
public void startScan(
        List<ScanFilter> filters, ScanSettings settings, final ScanCallback callback) {
    startScan(filters, settings, null, callback, /* callbackIntent= */ null);
}

三参数版本startScan转到5参数版本。

/packages/modules/Bluetooth/framework/java/android/bluetooth/le/BluetoothLeScanner.java
private int startScan(
        List<ScanFilter> filters,
        ScanSettings settings,
        final WorkSource workSource,
        final ScanCallback callback,
        final PendingIntent callbackIntent) {
    //从3参数版本可看到,此处的workSource、callbackIntent。callback、callbackIntent不能同时是null。
    BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); //确保蓝牙适配器已启用
    if (callback == null && callbackIntent == null) {
        throw new IllegalArgumentException("callback is null");
    }
    if (settings == null) {
        throw new IllegalArgumentException("settings is null");
    }
    synchronized (mLeScanClients) { //在mLeScanClients的同步块中执行,以确保线程安全。
        if (callback != null && mLeScanClients.containsKey(callback)) {//检查扫描客户端是否已存在
            return postCallbackErrorOrReturn(
                    callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
        }
        IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
        if (gatt == null) {
            return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
        }
        if (!isSettingsConfigAllowedForScan(settings)) {
            return postCallbackErrorOrReturn(
                    callback, ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
        }
        if (!isHardwareResourcesAvailableForScan(settings)) {
            return postCallbackErrorOrReturn(
                    callback, ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
        }
        if (!isSettingsAndFilterComboAllowed(settings, filters)) {
            return postCallbackErrorOrReturn(
                    callback, ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
        }
        if (callback != null) {
         // 进这个入口
            BleScanCallbackWrapper wrapper =
                    new BleScanCallbackWrapper(gatt, filters, settings, workSource, callback);//封装回调逻辑
            wrapper.startRegistration(); //启动注册过程
        } else {
            try {
                final SynchronousResultReceiver recv = SynchronousResultReceiver.get();
                gatt.startScanForIntent(
                        callbackIntent, settings, filters, mAttributionSource, recv);
                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
            } catch (TimeoutException | RemoteException e) {
                return ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
            }
        }
    }
    return ScanCallback.NO_ERROR;
}

startScan方法的一个重载版本,它接受额外的WorkSource和PendingIntent参数。主要处理如下:

  1. 检查蓝牙适配器状态;

  2. 参数验证;

  3. 检查扫描客户端是否已存在;

  4. 获取IBluetoothGatt接口;

  5. 设置和过滤器组合验证:进行一系列检查以确保提供的settings和filters是有效的,并且符合当前硬件和系统的要求。

  6. 启动扫描:

  • 如果提供了callback,则创建一个BleScanCallbackWrapper来。

  • 如果提供了callbackIntent,则使用gatt.startScanForIntent方法启动扫描,并通过SynchronousResultReceiver同步等待扫描结果。

GATT是bt-jni中binder本地对象BluetoothGattBinder的代理对象。当“callback != null”时,创建BleScanCallbackWrapper,并调用startRegistration方法。

startRegistration

/packages/modules/Bluetooth/framework/java/android/bluetooth/le/BluetoothLeScanner.java
@SuppressWarnings("WaitNotInLoop") // TODO(b/314811467)
public void startRegistration() {
    synchronized (this) { //确保在同一时间只有一个线程可以执行此方法的代码块,为了防止并发问题,特别是当多个线程尝试同时注册扫描器时
        // Scan stopped.
        if (mScannerId == -1 || mScannerId == -2) return;
        try {
            final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); //获取一个用于接收注册结果的对象。这个对象将用于等待注册操作的结果
            // 调用BluetoothGattBinder.registerScanner。
            mBluetoothGatt.registerScanner(this, mWorkSource, mAttributionSource, recv); //尝试注册扫描器
            recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
            wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
        } catch (TimeoutException | InterruptedException | RemoteException e) {
            Log.e(TAG, "application registration exception", e);
            postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
        }
        if (mScannerId > 0) {
            mLeScanClients.put(mScanCallback, this);
        } else {
            // Registration timed out or got exception, reset RscannerId to -1 so no
            // subsequent operations can proceed.
            if (mScannerId == 0) mScannerId = -1;

            // If scanning too frequently, don't report anything to the app.
            if (mScannerId == -2) return;

            postCallbackError(
                    mScanCallback,
                    ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
        }
    }
}

startRegistration方法用于尝试注册扫描器,以便可以开始BLE扫描操作。

  1. 检查扫描器状态;

  2. 获取结果接收器;

  3. 注册扫描器:调用mBluetoothGatt.registerScanner方法尝试注册扫描器。这个方法需要当前对象(this)、工作源(mWorkSource)、归因源(mAttributionSource)和结果接收器(recv)作为参数。

  4. 等待结果:使用recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null)等待注册结果。这里设置了一个超时时间(通过getSyncTimeout()获取),如果超时或结果被中断,则会抛出异常。

  5. 等待回调:调用wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS)进一步等待可能的回调。这个等待是为了确保在继续之前,所有相关的回调都被处理完毕。

  6. 注册成功:如果mScannerId大于0,表示注册成功,将当前扫描回调添加到mLeScanClients映射中。

  7. 注册失败:如果mScannerId为0(理论上不应发生,因为通常会有超时或异常),则将其重置为-1。如果mScannerId为-2(表示扫描太频繁),则不向应用报告任何内容并直接返回。对于其他失败情况,通过postCallbackError方法通知扫描回调接口注册失败。

总之,startRegistration的主要任务是调用mBluetoothGatt.registerScanner,即BluetoothGattBinder.registerScanner。至此进入bluetooth apk。

2. Bluetooth Servicec层:registerScanner

/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/gatt/GattService.java
 static class BluetoothGattBinder extends IBluetoothGatt.Stub
    implements IProfileServiceBinder {
     private void registerScanner(IScannerCallback callback, WorkSource workSource,
        AttributionSource attributionSource) throws RemoteException {
        GattService service = getService();
        if (service == null) {
            return;
        }
        service.registerScanner(callback, workSource, attributionSource);
    }
    

    void registerScanner(IScannerCallback callback, WorkSource workSource,
        AttributionSource attributionSource) throws RemoteException {
    if (!Utils.checkScanPermissionForDataDelivery(
            this, attributionSource, "GattService registerScanner")) {
        return;
    }

    UUID uuid = UUID.randomUUID(); //为每个扫描器注册生成一个唯一的UUID,有助于跟踪和管理不同的扫描请求
    if (DBG) {
        Log.d(TAG, "registerScanner() - UUID=" + uuid);
    }

    enforceImpersonatationPermissionIfNeeded(workSource);

    AppScanStats app = mTransitionalScanHelper.getScannerMap()
                .getAppScanStatsByUid(Binder.getCallingUid());
    //检查调用应用是否过于频繁地发起扫描。如果扫描频率过高且应用没有特权权限,则拒绝注册并通知调用者失败
    if (app != null && app.isScanningTooFrequently() 
            && !Utils.checkCallerHasPrivilegedPermission(this)) {
        Log.e(TAG, "App '" + app.appName + "' is scanning too frequently");
        callback.onScannerRegistered(ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY, -1);
        return;
    }

    mTransitionalScanHelper
        .getScannerMap().add(uuid, workSource, callback, null, this);
    mScanManager.registerScanner(uuid);
}

BluetoothGattBinder.registerScanner调用service.registerScanner,sevice是GattService。GattService.registerScanner主要两个操作。

  1. 随机生成一个uuid,类似connectGatt,将作为唯一标识,用于在扫描client集合中作为key。

  2. mScanManager.registerScanner(uuid)。调用ScanManager.registerScanner,继续注册Scanner。

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_scan/ScanManager.java
public void registerScanner(UUID uuid) {
    mScanNative.registerScanner(uuid.getLeastSignificantBits(),
            uuid.getMostSignificantBits());
}

ScanManager.registerScanner调用mScanNative.registerScannerNative,此至要转入c/c++代码。

3. JNI层:registerScannerNative

packages/modules/Bluetooth/android/app/jni/com_android_bluetooth_gatt.cpp
static void registerScannerNative(JNIEnv* /* env */, jobject /* object */,
                                  jlong app_uuid_lsb, jlong app_uuid_msb) {
  if (!sGattIf) return;

  Uuid uuid = from_java_uuid(app_uuid_msb, app_uuid_lsb);
  sGattIf->scanner->RegisterScanner(
      uuid, base::Bind(&btgattc_register_scanner_cb, uuid));
}

调用sGattIf->scanner->RegisterScanner方法,将转换后的UUID和一个回调函数(通过base::Bind绑定到btgattc_register_scanner_cb函数)传递给协议栈的扫描器组件进行注册。这里的回调函数用于处理扫描器注册成功或失败的情况。

sGattIf指向bt协议栈的btgattInterface。sGattIf->scanner指向bt协议栈的get_ble_scanner_instance()。

4. Bluedroid协议栈:btLeScannerInstance->RegisterScanner

a. btif_gatt_get_interface
/packages/modules/Bluetooth/system/btif/src/btif_gatt.cc
const btgatt_interface_t* btif_gatt_get_interface() {
  // TODO(jpawlowski) right now initializing advertiser field in static
  // structure cause explosion of dependencies. It must be initialized here
  // until those dependencies are properly abstracted for tests.
  btgattInterface.scanner = get_ble_scanner_instance();
  btgattInterface.advertiser = bluetooth::shim::get_ble_advertiser_instance();
  btgattInterface.distance_measurement_manager =
      bluetooth::shim::get_distance_measurement_instance();
  return &btgattInterface;
}
b. get_ble_scanner_instance
packages/modules/Bluetooth/system/btif/src/btif_ble_scanner.cc
BleScannerInterface* get_ble_scanner_instance() {
  return bluetooth::shim::get_ble_scanner_instance();
}
packages/modules/Bluetooth/system/main/shim/le_scanning_manager.cc
BleScannerInterfaceImpl* bt_le_scanner_instance = nullptr;

BleScannerInterface* bluetooth::shim::get_ble_scanner_instance() {
  if (bt_le_scanner_instance == nullptr) {
    bt_le_scanner_instance = new BleScannerInterfaceImpl();
  }
  return bt_le_scanner_instance;
}

bt_le_scanner_instance是个指向BleScannerInterface指针,BleScannerInterfaceImpl是一个class,不是c struct,scanner->RegisterScanner(...)是调用BleScannerInterfaceImpl成员函数RegisterScanner。

4.1. BleScannerInterfaceImpl::RegisterScanner

packages/modules/Bluetooth/system/main/shim/le_scanning_manager.cc
/** Registers a scanner with the stack */
void BleScannerInterfaceImpl::RegisterScanner(const bluetooth::Uuid& uuid,
                                              RegisterCallback) {
  LOG(INFO) << __func__ << " in shim layer";
  auto app_uuid = bluetooth::hci::Uuid::From128BitBE(uuid.To128BitBE());
  bluetooth::shim::GetScanning()->RegisterScanner(app_uuid);
}

RegisterScanner将任务委托给了另一个通过bluetooth::shim::GetScanning() 获取的对象,将转换后的UUID传递给BLE的扫描管理器进行注册。GetScanning返回一个指向管理BLE扫描的对象的指针。

/packages/modules/Bluetooth/system/gd/hci/le_scanning_manager.cc
void LeScanningManager::RegisterScanner(Uuid app_uuid) {
  CallOn(pimpl_.get(), &impl::register_scanner, app_uuid);
}

使用CallOn来调用impl成员(一个指向实现的智能指针)上的register_scanner 方法。

4.2. register_scanner

/packages/modules/Bluetooth/system/gd/hci/le_scanning_manager.cc
 void register_scanner(const Uuid app_uuid) {
    for (uint8_t i = 1; i <= kMaxAppNum; i++) { //检查重复注册
      if (scanners_[i].in_use && scanners_[i].app_uuid == app_uuid) {
        LOG_ERROR("Application already registered %s", app_uuid.ToString().c_str());
        scanning_callbacks_->OnScannerRegistered(app_uuid, 0x00, ScanningCallback::ScanningStatus::INTERNAL_ERROR);
        return;
      }
    }

    // valid value of scanner id : 1 ~ kMaxAppNum
    for (uint8_t i = 1; i <= kMaxAppNum; i++) { //寻找空闲扫描器ID
      if (!scanners_[i].in_use) {
        scanners_[i].app_uuid = app_uuid;
        scanners_[i].in_use = true;
        scanning_callbacks_->OnScannerRegistered(app_uuid, i, ScanningCallback::ScanningStatus::SUCCESS);
        return;
      }
    }

    LOG_ERROR("Unable to register scanner, max client reached:%d", kMaxAppNum);
    scanning_callbacks_->OnScannerRegistered(app_uuid, 0x00, ScanningCallback::ScanningStatus::NO_RESOURCES);
  }

注册一个新的BLE扫描器实例给指定的应用程序。通过检查scanners_数组来管理扫描器的注册状态。

5. 开始回调OnScannerRegistered

先看注册扫描回调的地方

/packages/modules/Bluetooth/system/main/shim/le_scanning_manager.cc
void BleScannerInterfaceImpl::RegisterCallbacks(ScanningCallbacks* callbacks) {
  LOG(INFO) << __func__ << " in shim layer";
  scanning_callbacks_ = callbacks;
}

5.1. OnScannerRegistered

/packages/modules/Bluetooth/system/gd/rust/topshim/gatt/gatt_ble_scanner_shim.cc
// ScanningCallbacks implementations

void BleScannerIntf::OnScannerRegistered(const bluetooth::Uuid app_uuid, uint8_t scannerId, uint8_t status) {
  rusty::gdscan_on_scanner_registered(reinterpret_cast<const signed char*>(&app_uuid), scannerId, status);
}

BleScannerIntf::OnScannerRegistered是一个回调函数实现,它是 BleScannerIntf接口的一部分,用于处理BLE扫描器注册的结果。这个函数接收三个参数:应用程序的UUID、扫描器ID和注册状态。然后,它将这些信息传递给Rust代码,通过调用rusty::gdscan_on_scanner_registered函数实现。

具体地,这个函数首先将 app_uuid的地址转换为 const signed char* 类型,因为Rust与C++之间的接口需要明确的数据类型转换,尤其是当涉及到指针和复杂数据结构时。在这里,reinterpret_cast被用来重新解释 app_uuid的地址,将其视为指向 signed char的常量指针。因为Rust代码期望以这种方式接收UUID数据,这是为了满足Rust与C++之间特定ABI(应用程序二进制接口)的要求。

然而,将 Uuid类型的地址直接转换为 signed char*可能不是处理UUID数据的最佳方式,因为这取决于 Uuid 类型的内部表示和Rust代码期望的格式。

在实际应用中,更常见的做法是将UUID转换为字节数组(例如,一个16字节的数组),然后将该数组传递给Rust代码。不过,这里直接使用 reinterpret_cast 可能是因为某些内部约定或性能考虑。

总的来说,这段代码展示了如何在C++和Rust之间传递复杂数据(如UUID)和简单数据类型(如整数),并通过回调函数机制实现跨语言的交互。

/packages/modules/Bluetooth/system/gd/rust/topshim/src/profiles/gatt.rs
cb_variant!(
    GDScannerCb,
    gdscan_on_scanner_registered -> GattScannerCallbacks::OnScannerRegistered,
    *const i8, u8, u8 -> GattStatus, {
        let _0 = unsafe { *(_0 as *const Uuid).clone() };
    }
);

5.2. BleScannerInterfaceImpl::OnScannerRegistered

/packages/modules/Bluetooth/system/main/shim/le_scanning_manager.cc
void BleScannerInterfaceImpl::OnScannerRegistered(
    const bluetooth::hci::Uuid app_uuid, bluetooth::hci::ScannerId scanner_id,
    ScanningStatus status) {
  auto uuid = bluetooth::Uuid::From128BitBE(app_uuid.To128BitBE());
  do_in_jni_thread(FROM_HERE,
                   base::BindOnce(&ScanningCallbacks::OnScannerRegistered,
                                  base
  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android中,可以使用Bluetooth Low Energy(BLE)API扫描、连接和断开连接BLE设备。以下是一些基本步骤: 1. 获取 BluetoothAdapter 对象:使用 BluetoothManager 获取 BluetoothAdapter 对象。 ```java BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); ``` 2. 检查蓝牙是否已启用:使用 isEnabled() 方法检查蓝牙是否已启用。 ```java if (!bluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } ``` 3. 开始扫描:使用 startScan() 方法开始扫描BLE设备。可以使用 BluetoothLeScanner 对象进行扫描。 ```java BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner(); scanner.startScan(mScanCallback); ``` 4. 停止扫描:使用 stopScan() 方法停止扫描BLE设备。 ```java scanner.stopScan(mScanCallback); ``` 5. 连接设备:使用 BluetoothDevice 对象连接BLE设备。可以使用 connectGatt() 方法进行连接。 ```java BluetoothGatt mBluetoothGatt = bluetoothDevice.connectGatt(this, false, mGattCallback); ``` 6. 断开连接:使用 disconnect() 方法断开连接。 ```java mBluetoothGatt.disconnect(); ``` 7. 关闭 GATT 连接:使用 close() 方法关闭 GATT 连接。 ```java mBluetoothGatt.close(); ``` 以上是一些基本步骤来扫描、连接和断开连接BLE设备。具体实现还需要根据具体需求进行调整和完善。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值