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

异常描述

在蓝牙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.java
    boolean 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() 方法查看系统服务:

   @UnsupportedAppUsage
    public @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.java
    private 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 开发

猜你喜欢

转载自blog.csdn.net/CJohn1994/article/details/127757286