Android HID设备的连接

Hid是Human Interface Device的缩写,由其名称可以了解HID设备是直接与人交互的设备,例如键盘、鼠标与游戏手柄等。
我们知道在手机设置--蓝牙功能界面可以手动搜索蓝牙HID设备并进行连接,这篇博客就是介绍如何在android代码中实现HID设备的连接。最后会给出完整的代码工程。一个前提条件是android4.0以上才支持HID设备。
android手机与蓝牙HID设备连接的步骤:
1.开启蓝牙功能
2.手机搜索蓝牙HID设备
3.得到BluetoothDevice,配对HID设备
4.连接HID设备
了解过蓝牙开发的同学相信前面3个步骤都不是问题,本文会重点介绍第四个步骤,前面的步骤简单列出来。
1.开启蓝牙功能

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    Toast.makeText(this,"不支持蓝牙功能",0).show();
    //不支持蓝牙
    return;
}
//如果没有打开蓝牙
if (!mBluetoothAdapter.isEnabled()) {
    mBluetoothAdapter.enable();
}

2.手机搜索蓝牙HID设备

//扫描蓝牙设备
if (!mBluetoothAdapter.isDiscovering()) {
    mBluetoothAdapter.startDiscovery();
}

3.配对HID设备
搜索到蓝牙设备后系统会发送这个广播
BluetoothDevice.ACTION_FOUND
通过监听这个广播就可以得到BluetoothDevice
//通过广播接收到了BluetoothDevice

final BluetoothDevice localBluetoothDevice = (BluetoothDevice) intent
        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

然后通过反射配对BluetoothDevice

/**
 * 配对
 * @param bluetoothDevice
    */
public void pair(BluetoothDevice bluetoothDevice) {
   device = bluetoothDevice;
   Method createBondMethod;
   try {
      createBondMethod = BluetoothDevice.class.getMethod("createBond");
      createBondMethod.invoke(device);
   } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
   }

}

相信以上步骤大家都十分了解了,下面最重要的部分来了。
4.连接HID设备
找遍所有公开的api中是没用方法可以直接连接HID设备的,既然手机设置-蓝牙界面可以连接HID设备,说明系统是可以做到的,那是不是把这个方法隐藏了,我们带着这个疑问去源码看看。
1)首先我们找到android/bluetooth/BluetoothProfile.class这个类
为什么是先找到这个类?
Bluetooth的一个很重要特性,就是所有的Bluetooth产品都无须实现全部的Bluetooth规范。为了更容易的保持Bluetooth设备之间的兼容,Bluetooth规范中定义了Profile。Profile定义了设备如何实现一种连接或者应用,你可以把Profile理解为连接层或者应用层协议。
所以我们要先确认HID设备属于哪种Profile。

/**
 * Input Device Profile
 * @hide
 */
public static final int INPUT_DEVICE = 4;

找到定义的INPUT_DEVICE,这就是我们HID设备的Profile,字面意思就是输入设备。
2)找到BluetoothProfile的子类BluetoothInputDevice
查看到该类里面的connect方法,入参BluetoothDevice,我们通过步骤三已经得到BluetoothDevice,我们大胆猜想是不是调用这个方法就可以实现连接了呢。

/**
 * Initiate connection to a profile of the remote bluetooth device.
 *
 * <p> The system supports connection to multiple input devices.
 *
 * <p> This API returns false in scenarios like the profile on the
 * device is already connected or Bluetooth is not turned on.
 * When this API returns true, it is guaranteed that
 * connection state intent for the profile will be broadcasted with
 * the state. Users can get the connection state of the profile
 * from this intent.
 *
 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
 * permission.
 *
 * @param device Remote Bluetooth Device
 * @return false on immediate error,
 *               true otherwise
 * @hide
 */
public boolean connect(BluetoothDevice device) {
    if (DBG) log("connect(" + device + ")");
    if (mService != null && isEnabled() && isValidDevice(device)) {
        try {
            return mService.connect(device);
        } catch (RemoteException e) {
            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
            return false;
        }
    }
    if (mService == null) Log.w(TAG, "Proxy not attached to service");
    return false;
}

我们再来看下该类的说明

/**
 * This class provides the public APIs to control the Bluetooth Input
 * Device Profile.
 *
 *<p>BluetoothInputDevice is a proxy object for controlling the Bluetooth
 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
 * the BluetoothInputDevice proxy object.
 *
 *<p>Each method is protected with its appropriate permission.
 *@hide
 */

首先注意到该类是个隐藏类并不能直接调用,调用方法已经进行说明了。通过BluetoothAdapter#getProfileProxy方法得到隐藏类BluetoothInputDevice

3)BluetoothAdapter#getProfileProxy方法

* @param context Context of the application
 * @param listener The service Listener for connection callbacks.
 * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH},
 *                {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
 *                {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}.
 * @return true on success, false on error
 */
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
                               int profile)

第一个参数就不用说了,我们先看第三个参数 int profile,前面介绍了INPUT_DEVICE,这就是我们HID设备的Profile,我们看到也是隐藏字段,所以我们必须先通过反射获得这个参数。方法如下:

public static int getInputDeviceHiddenConstant() {
   Class<BluetoothProfile> clazz = BluetoothProfile.class;
   for (Field f : clazz.getFields()) {
      int mod = f.getModifiers();
      if (Modifier.isStatic(mod) && Modifier.isPublic(mod)
            && Modifier.isFinal(mod)) {
         try {
            if (f.getName().equals("INPUT_DEVICE")) {
               return f.getInt(null);
            }
         } catch (Exception e) {
         }
      }
   }
   return -1;
}

那我们再看第二个参数BluetoothProfile.ServiceListener,这个参数可以回调出BluetoothInputDevice,然后再反射connect方法。

/**
 *查看BluetoothInputDevice源码,connect(BluetoothDevice device)该方法可以连接HID设备,但是查看BluetoothInputDevice这个类
 * 是隐藏类,无法直接使用,必须先通过BluetoothProfile.ServiceListener回调得到BluetoothInputDevice,然后再反射connect方法连接
 *
 */
private BluetoothProfile.ServiceListener connect = new BluetoothProfile.ServiceListener() {
   @Override
   public void onServiceConnected(int profile, BluetoothProfile proxy) {
      //BluetoothProfile proxy这个已经是BluetoothInputDevice类型了
      try {
         if (profile == getInputDeviceHiddenConstant()) {
            if (device != null) {
               //得到BluetoothInputDevice然后反射connect连接设备
               Method method = proxy.getClass().getMethod("connect",
                     new Class[] { BluetoothDevice.class });
               method.invoke(proxy, device);
            }
         }
      } catch (Exception e) {
         // TODO Auto-generated catch block
         // e.printStackTrace();
      }
   }

   @Override
   public void onServiceDisconnected(int profile) {

   }
};

最后HID设备连接成功,事实证明我们的猜想是正确的。
项目下载地址:
https://github.com/liushenwenyuan/HIDConnect
http://download.csdn.net/detail/szydwy/9546246

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Android 编程中连接 HID 设备,需要使用 USB Host API。以下是一个简单的示例代码,可以帮助你开始: ```java public class MainActivity extends AppCompatActivity { private static final String TAG = "HID Demo"; private static final int VID = 0x1234; // Vendor ID of your HID device private static final int PID = 0x5678; // Product ID of your HID device private UsbManager mUsbManager; private UsbDevice mDevice; private UsbDeviceConnection mConnection; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); // Find the HID device by vendor ID and product ID for (UsbDevice device : mUsbManager.getDeviceList().values()) { if (device.getVendorId() == VID && device.getProductId() == PID) { mDevice = device; break; } } if (mDevice == null) { Log.e(TAG, "HID device not found!"); return; } // Request permission to access the HID device PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent("com.example.hid.USB_PERMISSION"), 0); mUsbManager.requestPermission(mDevice, permissionIntent); // Open the HID device connection mConnection = mUsbManager.openDevice(mDevice); if (mConnection == null) { Log.e(TAG, "Failed to open HID device!"); return; } // Claim the HID interface UsbInterface intf = mDevice.getInterface(0); mConnection.claimInterface(intf, true); // Start reading input reports from the HID device byte[] buffer = new byte[intf.getEndpoint(0).getMaxPacketSize()]; UsbRequest request = new UsbRequest(); request.initialize(mConnection, intf.getEndpoint(0)); request.queue(buffer, buffer.length); while (true) { UsbRequest response = mConnection.requestWait(); if (response != null && response.getEndpoint() == intf.getEndpoint(0)) { Log.i(TAG, "Received HID input report: " + Arrays.toString(buffer)); request.queue(buffer, buffer.length); } } } } ``` 在这个示例代码中,我们首先使用 USB Host API 找到 HID 设备,然后请求访问权限、打开设备连接、声明接口,并开始读取输入报告。你需要根据你的 HID 设备的 VID 和 PID 更新代码中的常量。注意,这个示例仅仅是一个起步示例,你需要根据你的具体需求进行更多的定制和调整。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值