异常描述

在蓝牙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. Gson将字符串转map时,int默认为double类型
  2. Qt Creator将应用程序部署到Android设备
  3. 训练日志 2019.3.10
  4. java并发AtomicIntegerArray
  5. 春节临近|传统彩色手绘年画素材,满满东方韵味
  6. 我的世界服务器公会系统,[娱乐|综合|RPG] [付费] Advanced Clans — 更好的公会系统 [1.8-1.15]...
  7. ofo 辟谣“月盈利百万”;苹果回应向腾讯传输数据;Python 3.8.0 发布 | 极客头条...
  8. 计算机技术应用论文参考,计算机技术应用参考论文(2)
  9. tomcat启动问题,卡在 preparing launch delegate 100% 的解决方法
  10. iec611313标准下载_iec611313编程标准.ppt
  11. 用防火墙自动拦截攻击IP
  12. 关于marked数组处理
  13. python识别验证码 免费API接口
  14. 织梦ajax登录界面,dede织梦后台登陆成功后又跳转到登陆页面
  15. 3岁孩子能力训练计划
  16. stp转obj, stp转stl转换小工具
  17. inner join 和outer join的区别
  18. mysql如何锁表和解锁
  19. 【CodeForces 332B --- Maximum Absurdity】递推
  20. IOS 移动端视频播放不全屏

热门文章

  1. 橄榄山+住建部BIM数据库:
  2. Kubernetes设计模式
  3. vue 中使用 QRCode.js 链接转二维码带中间图片
  4. 嵌入式:ARM异常中断指令SWI、BKPT、CLZ详解
  5. 影视解说怎么配音?免费配音软件分享,真人发声选择多样
  6. UI组件-UITextField
  7. c语言 raii 用法日志,陈硕的Blog
  8. 不要用ListView了,最好的选择RecyclerView
  9. echarts 饼图/环形图鼠标经过显示文本标签 图例icon
  10. php 中文朗读,能说会道 学生版 官方网站 | 功能最强大的语音朗读软件,支持全世界近30种语言...