Android蓝牙服务查找附近设备分析探索

作者:c小旭 时间:2023-04-01 19:50:21 

一、APP端调用

1、注册广播监听查找结果

//蓝牙发现设备和查找结束广播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(btReceiver, intentFilter);
BroadcastReceiver btReceiver = new BroadcastReceiver() {
   @Override
   public void onReceive(Context context, Intent intent) {
       if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) {
           BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
           if (device != null) {
               //这里收到的是单条设备信息,可以放到List中进行刷新列表
               //设备名称:device.getName()
               //设备地址:device.getAddress()
               if(device.getBondState() == BluetoothDevice.BOND_BONDED) {
                   //已配对设备
               }
           }
       } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
               //查找结束
       }
   }
};

2、开始查找附近设备

btAdapter.startDiscovery();

3、异常处理

上面代码无法接收到 BluetoothDevice.ACTION_FOUND 广播,查找资料发现 Android 6.0 后需要增加两个权限并且需要动态申请。

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M
        && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {//请求权限
   requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},1);
}

这里的权限动态申请就简单处理一下,不做详细介绍了。

另一个问题是收到发现设备广播数据,很多数据的 getName() 为 null,这里还需要做一些判空处理。

二、查找设备源码分析

1、BluetoothAdapter.startDiscovery()

private IBluetooth mService;
public boolean startDiscovery() {
   if (getState() != STATE_ON) {
       return false;
   }
   try {
       mServiceLock.readLock().lock();
       if (mService != null) {
           return mService.startDiscovery(mAttributionSource);
       }
   } catch (RemoteException e) {
       Log.e(TAG, "", e);
   } finally {
       mServiceLock.readLock().unlock();
   }
   return false;
}

可以看到这里直接调用 mService.startDiscovery(),IBluetooth 的实现类为 AdapterService,相较于蓝牙开关功能,省去了调用 BluetoothManagerService 的部分。

2、AdapterService.startDiscovery()

public boolean startDiscovery(AttributionSource attributionSource) {
   AdapterService service = getService();
   if (service == null || !callerIsSystemOrActiveUser(TAG, "startDiscovery")) {
       return false;
   }
   if (!Utils.checkScanPermissionForDataDelivery(
       service, attributionSource, "Starting discovery.")) {
       return false;
   }
   return service.startDiscovery(attributionSource);
}
boolean startDiscovery(AttributionSource attributionSource) {
   UserHandle callingUser = UserHandle.of(UserHandle.getCallingUserId());
   debugLog("startDiscovery");
   String callingPackage = attributionSource.getPackageName();
   mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
   boolean isQApp = Utils.isQApp(this, callingPackage);
   boolean hasDisavowedLocation = Utils.hasDisavowedLocationForScan(this, attributionSource, mTestModeEnabled);
   String permission = null;
   if (Utils.checkCallerHasNetworkSettingsPermission(this)) {
       permission = android.Manifest.permission.NETWORK_SETTINGS;
   } else if (Utils.checkCallerHasNetworkSetupWizardPermission(this)) {
       permission = android.Manifest.permission.NETWORK_SETUP_WIZARD;
   } else if (!hasDisavowedLocation) {
       if (isQApp) {
           if (!Utils.checkCallerHasFineLocation(this, attributionSource, callingUser)) {
               return false;
           }
           permission = android.Manifest.permission.ACCESS_FINE_LOCATION;
       } else {
           if (!Utils.checkCallerHasCoarseLocation(this, attributionSource, callingUser)) {
               return false;
           }
           permission = android.Manifest.permission.ACCESS_COARSE_LOCATION;
       }
   }
   synchronized (mDiscoveringPackages) {
       mDiscoveringPackages.add(new DiscoveringPackage(callingPackage, permission, hasDisavowedLocation));
   }
   return startDiscoveryNative();
}

这里可以看到权限验证相关的内容。最后调用 startDiscoveryNative() 进入 JNI 层,在com_android_bluetooth_btservice_AdapterService.cpp文件中,调用startDiscoveryNative方法。

3、startDiscoveryNative()

源码位置:packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
   ALOGV("%s", __func__);
   if (!sBluetoothInterface) return JNI_FALSE;
   int ret = sBluetoothInterface->start_discovery();
   return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

4、start_discovery() 扫描入口

源码位置:system/bt/btif/src/bluetooth.cc

static int start_discovery(void) {
   if (!interface_ready()) return BT_STATUS_NOT_READY;

do_in_main_thread(FROM_HERE, base::BindOnce(btif_dm_start_discovery));
   return BT_STATUS_SUCCESS;
}

5、btif_dm_start_discovery() 配置参数

源码位置:/system/bt/btif/src/btif_dm.cc

设备管理(DM)相关的功能

void btif_dm_start_discovery(void) {
   ......
   /* Will be enabled to true once inquiry busy level has been received */
   btif_dm_inquiry_in_progress = false;
   /* find nearby devices */
   BTA_DmSearch(btif_dm_search_devices_evt, is_bonding_or_sdp());
}

6、BTA_DmSearch() 搜索对等蓝牙设备

源码位置:/system/bt/bta/dm/bta_dm_api.cc

它执行查询并获取设备的远程名称。

void BTA_DmSearch(tBTA_DM_SEARCH_CBACK* p_cback, bool is_bonding_or_sdp) {
   tBTA_DM_API_SEARCH* p_msg = (tBTA_DM_API_SEARCH*)osi_calloc(sizeof(tBTA_DM_API_SEARCH));
   /* Queue request if a device is bonding or performing service discovery */
   if (is_bonding_or_sdp) {
       p_msg->hdr.event = BTA_DM_API_QUEUE_SEARCH_EVT;
   } else {
       p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
   }
   p_msg->p_cback = p_cback;
   bta_sys_sendmsg(p_msg);
}

7、bta_sys_sendmsg() 发送扫描消息

源码位置:/system/bt/bta/sys/bta_sys_main.cc

向 BTU TASK 发送扫描消息,由蓝牙设备管理模块处理。

void bta_sys_sendmsg(void* p_msg) {
   if (do_in_main_thread(
           FROM_HERE,
           base::Bind(&bta_sys_event, static_cast<BT_HDR_RIGID*>(p_msg))) !=
       BT_STATUS_SUCCESS) {
     LOG(ERROR) << __func__ << ": do_in_main_thread failed";
   }
}

这里调用的是 do_in_main_thread() 方法,这个方法其实在 4 中已经调用过一次。这个方法只是返回一个状态。

位置:/system/bt/stack/btu/btu_task.cc

bt_status_t do_in_main_thread(const base::Location& from_here, base::OnceClosure task) {
   if (!main_thread.DoInThread(from_here, std::move(task))) {
     LOG(ERROR) << __func__ << ": failed from " << from_here.ToString();
     return BT_STATUS_FAIL;
   }
   return BT_STATUS_SUCCESS;
}

BTU TASK收到消息后,调用 bta_dm_main.c 的(用于DM的状态机事件处理函数) bta_dm_search_sm_execute() 执行状态切换和 inquiry 流程。这里就不往下分析了。

来源:https://blog.csdn.net/c19344881x/article/details/128723883

标签:Android,蓝牙,查找附近设备
0
投稿

猜你喜欢

  • C#微信公众号与订阅号接口开发示例代码

    2023-07-20 09:11:07
  • C#基于基姆拉尔森算法计算指定日期是星期几的方法

    2022-04-28 09:31:25
  • mybatis-plus使用generator实现逆向工程

    2022-06-05 20:16:49
  • Java经典排序算法之插入排序

    2022-08-29 11:08:39
  • WCF实现的计算器功能实例

    2022-05-07 23:17:12
  • Java maven三种仓库,本地仓库,私服,中央仓库的配置

    2023-04-13 12:05:17
  • Admin - SpringBoot + Maven 多启动环境配置实例详解

    2022-03-08 09:48:16
  • SpringBoot整合Mybatis实现CRUD

    2022-01-10 09:26:45
  • IntelliJ IDEA修改新建文件自动生成注释的user名

    2021-12-29 04:27:38
  • Android调用系统的发邮件功能的小例子

    2022-09-09 04:23:11
  • Android中基于XMPP协议实现IM聊天程序与多人聊天室

    2022-05-06 07:37:51
  • Android实现九宫格拼图游戏

    2021-06-11 09:41:13
  • Java中EasyPoi多sheet导出功能实现

    2023-01-15 08:10:39
  • SpringBoot实现埋点监控

    2022-11-27 06:32:07
  • Android使用ListView实现下拉刷新及上拉显示更多的方法

    2023-01-10 04:29:45
  • C# Split分隔字符串的应用(C#、split、分隔、字符串)

    2021-12-01 23:13:51
  • Android自定义EditText右侧带图片控件

    2023-08-30 03:09:47
  • 详细解读Java编程中面向字符的输入流

    2023-09-27 10:39:21
  • C#如何使用Task执行异步操作

    2023-01-12 03:34:41
  • Java基础题新手练习(二)

    2022-03-10 00:11:57
  • asp之家 软件编程 m.aspxhome.com