异常描述

在蓝牙HID的开发过程中,使用红米K30手机 MIUI12.5(Android 11) 系统,打算将手机打造成蓝牙外设(键盘、触摸板、游戏手柄等)。首先调用下面的方式与系统蓝牙HID服务绑定:

mBtAdapter.getProfileProxy(mContext, mServiceListener, BluetoothProfile.HID_DEVICE);

出现下面的错误信息

Could not bind to Bluetooth Service with Intent { act=android.bluetooth.IBluetoothHidDevice }

上述报错后就不会与系统蓝牙HID服务绑定,从而无法得到 BluetoothHidDevice 进行注册。而使用 BluetoothProfile.A2DP 绑定时则无此问题。

源码分析

方法 BluetoothAdapter.getProfileProxy() 的代码如下:

    public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,int profile) {if (context == null || listener == null) {return false;}if (profile == BluetoothProfile.HEADSET) {BluetoothHeadset headset = new BluetoothHeadset(context, listener, this);return true;} else if (profile == BluetoothProfile.A2DP) {...代码省略} else if (profile == BluetoothProfile.HID_DEVICE) {BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this);return true;} else if (profile == BluetoothProfile.HEARING_AID) {...代码省略}

根据 BluetoothProfile.HID_DEVICE 创建 BluetoothHidDeviceBluetoothHidDevice 构造函数中会调用 doBind() 与系统蓝牙应用的蓝牙HID服务进行绑定,在Android 9 源码如下:

/frameworks/base/core/java/android/bluetooth/BluetoothHidDevice.java

    //BluetoothHidDevice.javaboolean doBind() {Intent intent = new Intent(IBluetoothHidDevice.class.getName());ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);intent.setComponent(comp);if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,mContext.getUser())) {Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);return false;}Log.d(TAG, "Bound to HID Device Service");return true;}

android.bluetooth.IBluetoothHidDevice 作为 Action 与系统应用的蓝牙服务绑定。
Intent.resolveSystemService() 方法查看系统服务:

   @UnsupportedAppUsagepublic @Nullable ComponentName resolveSystemService(@NonNull PackageManager pm,@PackageManager.ComponentInfoFlags int flags) {if (mComponent != null) {return mComponent;}List<ResolveInfo> results = pm.queryIntentServices(this, flags);if (results == null) {return null;}ComponentName comp = null;for (int i=0; i<results.size(); i++) {ResolveInfo ri = results.get(i);if ((ri.serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {continue;}ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName,ri.serviceInfo.name);if (comp != null) {throw new IllegalStateException("Multiple system services handle " + this+ ": " + comp + ", " + foundComp);}comp = foundComp;}return comp;}

Intent.resolveSystemService() 的方法是系统用于解决系统应用程序服务意向的特殊功能。如果存在与Intent的多个潜在匹配,则引发异常。如果没有匹配项,则返回null。
查找不到 android.bluetooth.IBluetoothHidDevice 作为 Action的系统蓝牙HID服务将 ComponentName 为 null ,因此出现 “Could not bind to Bluetooth HID Device Service with Intent { act=android.bluetooth.IBluetoothHidDevice }” 的报错。

Android 10 不直接调用 doBind() 方法进行绑定,而是增加 BluetoothProfileConnector 进行绑定,源码如下:

/frameworks/base/core/java/android/bluetooth/BluetoothProfileConnector.java

    //BluetoothProfileConnector.javaprivate boolean doBind() {synchronized (mConnection) {if (mService == null) {logDebug("Binding service...");try {Intent intent = new Intent(mServiceName);ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);intent.setComponent(comp);if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,UserHandle.CURRENT_OR_SELF)) {logError("Could not bind to Bluetooth Service with " + intent);return false;}} catch (SecurityException se) {logError("Failed to bind service. " + se);return false;}}}return true;}

Android 9 和 Android 10 的错误打印不一致,Android 9 为 “Could not bind to Bluetooth HID Device Service with”, 而 Android 10 以上为 "Could not bind to Bluetooth Service with "。
但不管哪个版本都是与系统蓝牙应用的HID服务进行绑定。

packages\apps\Bluetooth\src\com\android\bluetooth\hid\HidDeviceService.java

系统中找不到蓝牙服务、无法绑定、或者蓝牙应用异常同样也无法正常获取服务注册蓝牙HID。

判断设备是否支持蓝牙HID

根据上述 Intent.resolveSystemService() 的方法,第三方应用可以执行下面方法判断设备是否有蓝牙HID服务来,返回 true 代表支持第三方应用注册蓝牙HID,反之当前设备的系统不支持。

    public boolean isSupportBluetoothHid() {Intent intent = new Intent("android.bluetooth.IBluetoothHidDevice");List<ResolveInfo> results = pm.queryIntentServices(intent, 0);if (results == null) {return false;}ComponentName comp = null;for (int i=0; i<results.size(); i++) {ResolveInfo ri = results.get(i);if ((ri.serviceInfo.applicationInfo.flags& ApplicationInfo.FLAG_SYSTEM) == 0) {continue;}ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName,ri.serviceInfo.name);if (comp != null) {throw new IllegalStateException("Multiple system services handle " + this+ ": " + comp + ", " + foundComp);}comp = foundComp;}return comp != null;}

解决方案

红米K30手机出现此问题可能是本身小米系统移植存在差异导致的,升级MIUI13(Android 12)后无此报错。
由于没有小米系统的源码,无法进行分析修改。只能尝试升级其他系统版本,根据上述 判断设备是否支持蓝牙HID 来识别当前系统的支持情况。
如果当前系统支持HID,那么通过调用 BluetoothHidDevice.registerApp() 方法 将回调 onAppStatusChanged() 状态 registered 为 true 时,如下:

onAppStatusChanged: pluggedDevice:null registered:true

这代表注册成功,通过另一个设备查找你的手机进行配对,需要调用 setScanMode 的模式让另一设备发现并连接,具体方式可查看另一篇博文 蓝牙HID——将android设备变成蓝牙键盘(BluetoothHidDevice)。配对成功后即可充当HID设备控制另一个终端设备。

在 Android 9 之前只有系统应用才可以注册 HID ,Android 9 之前的系统版本参考此篇博客 Android 蓝牙开发(三)蓝牙Hid 开发

蓝牙HID——Android手机注册HID时出现 Could not bind to Bluetooth (HID Device) Service with Intent * 的问题分析相关推荐

  1. android 添加通讯录联系人头像,Android 手机通讯录开发时给联系人添加头像失败的坑...

    Android 手机通讯录开发时给联系人添加头像失败的坑,在给联系人添加头像代码 Bitmap photo = ...; // 将 Bitmap 转为 byte[] byte[] bytes = Ls ...

  2. android手机连接电脑时直接截屏到电脑

    如题,android手机连接mac,直接快速截屏到mac. 解决方法: 利用android的adb命令即可: #截取手机屏幕保存到SDCard adb shell /system/bin/screen ...

  3. php推送手机,PHP_解析php做推送服务端实现ios消息推送,准备工作1.获取手机注册应用 - phpStudy...

    解析php做推送服务端实现ios消息推送 准备工作1.获取手机注册应用的deviceToken(iphone手机注册应用时返回唯一值deviceToken) 2.获取ck.pem文件(做手机端的给) ...

  4. android恢复联系人,如何从Android手机恢复联系人[最佳方式]

    有没有简单的方法 从Android设备恢复已删除/丢失的联系人? 各种原因将导致Android手机上的数据丢失. 例如,意外删除可能会导致电话号码丢失,而一些错误的操作也可能导致联系人丢失. 此外,技 ...

  5. Android手机的驱动安装

    Android手机连接电脑时,一般情况下,电脑会自动搜索并安装相应的驱动,但如果自动搜索安装不成功,就需要我们手动来安装.以下是手动安装流程: 1.去手机官网下载对应机型的USB驱动,如有必要,进行解 ...

  6. chrome inspect联调android手机webview和web h5遇坑

    adb unauthorized C:\Users\xxx.android里面删掉adbkey 和adbkey.pub两个文件,然后重新插拔手机 执行: adb kill-server adb sta ...

  7. android设备登录工行卡,工行Android手机银行怎么登陆?

    工行Android手机银行首次登录步骤:您在Android主界面点击工行手机银行客户端图标,进入登录页面,输入手机号和登录密码,点击"登录"键. 非首次登录步骤:登录页面手机号输入 ...

  8. 买android手机,买Android手机理由:iPhone 6竟无言以对

    如果用户在选购Android手机和iPhone时陷入两难境地,那么不妨考虑以下一些因素.以下7大理由表明,选择没那么困难,你应当选购Android手机而非iPhone. 1.Android手机与谷歌多 ...

  9. Android手机连接蓝牙播放时,蓝牙设备如何显示歌名、专辑、歌手等信息?

    转眼间,在XX音乐(国内著名音乐APP公司)工作了1年多了,作为Android多媒体开发的主力,必须奉上一点知识了. 当这个问题客服反馈给我的时候,我一脸懵逼,面对各种前辈们留下的坑,我必须说,我一定 ...

最新文章

  1. 操作系统(十九)进程互斥的软件实现方法
  2. KMP模式串匹配+Compress Words CodeForces - 1200E
  3. pcf8523_PCF上的Spring Cloud合同和Spring Cloud Services
  4. php file_get_contents 效率,php 浅析file_get_contents、curl 的效率和稳定性
  5. 如何用20%精力搞定80%任务
  6. Microsoft Office Visio 2007 下载
  7. vue 登录页面背景 - 动态粒子特效包括vue3使用 (Vue-Particles)
  8. 综述 | 一文看尽三种针对人工智能系统的攻击技术及防御策略
  9. PMP项目管理专业人员资格认证
  10. 如何把自己打造成为一名金领架构师-基础篇(一)
  11. 记录一次修复知网学位论文目录下载油猴脚本的过程
  12. 【运筹学】模型速览及推荐学习视频
  13. java如何开发视频软件_使用JAVA编写视频播放器
  14. 【C++登山之路之初露锋芒 2】——内联函数+ 引用+auto+nullptr关键字(万字详解,图片演示,结构原理)
  15. swift代码转oc代码_什么是不安全的Swift代码?
  16. zabbix4.0配置钉钉机器人告警详细教程
  17. java+jna+DD实现全局键鼠驱动级模拟
  18. win7系统无法连接局域网服务器,win7系统无法访问局域网的解决步骤
  19. 加利福尼亚大学研究报告:让AI自我怀疑以防人类被颠覆
  20. SRGAN模型——pytorch实现

热门文章

  1. COM服务器应用程序本地激活权限错误解决方法
  2. 公司已经3个月发不出工资了。没有钱招不到人,老项目一堆烂摊子。
  3. Python(三)数字日期和时间
  4. 17-阿里云服务器ECS使用教程之Web环境的搭建
  5. 全面剖析redis分布式锁
  6. mybatis 报错There is no getter for property named 'XXX' in 'class com.xx.xx'
  7. web项目部署架构图
  8. cocos2dx3.3显示中文
  9. 计算机可以编程游戏吗,我的世界真正的大神,在游戏中编程,制作出能玩的单机游戏...
  10. 微信小程序商城搭建,微信小程序商城源码,微信小程序商城项目