android - NDK 级别的 Android 低级 USB API,可在 Qt Android 上使用

标签 android qt android-ndk usb hid

我需要在 Android 上与 HID 设备交互。问题是我使用的是 Qt Android,我没有使用 Java UsbManager 类。

是否有任何我可以链接的 C 库,以便在无需使用 Java API 的情况下与 Android 上的 HID 设备通信?

我发现了这个:

http://source.android.com/devices/reference/bt__hh_8h_source.html

这似乎是一个定义 HID 通信的 header ,但我找不到关联的库。有什么想法吗?

提前致谢

最佳答案

我找到了一种方法。就我而言,我正在为一个设备开发一个控制面板,我需要它在每个设备上工作,而无需对设备进行 root。

基本上,我使用 UsbManager 来查找和获取设备。然后我打开设备并从 UsbDeviceConnection 调用 getFileDescriptor() 方法。然后我将这个 int 传递给 native 代码端。从那里我可以使用任何类型的请求轻松地从设备获取数据,而不必通过 JNI 将数据从 native 代码传递到 Java,反之亦然,这是一项缓慢而缓慢的工作。

最棘手的部分实际上是执行需要特殊格式的 ioctl 调用。

这些代码的一部分已经在 libusb 源代码上得到了很好的示例,因为这是它们实现对 linux 内核的 libsub 调用的方式。

如果有人知道这个问题的更好解决方案,请告诉我。

带有 USBManager Hook 的 Android Activity 代码:

public class MyActivity extends QtActivity
{
    private static MyActivity m_instance;
    private UsbAccessory accessory;
    private String TAG = "TAG";
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
    private PendingIntent mPermissionIntent;
    private UsbManager manager;
    private UsbDeviceConnection connection;
    private HashMap<Integer, Integer> connectedDevices;

    public MyActivity()
    {
        m_instance = this;

        connectedDevices = new HashMap<Integer, Integer>();
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        manager = (UsbManager) getSystemService(Context.USB_SERVICE);

        registerReceiver(usbManagerBroadcastReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED));
        registerReceiver(usbManagerBroadcastReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
        registerReceiver(usbManagerBroadcastReceiver, new IntentFilter(ACTION_USB_PERMISSION));

        mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);

        final Handler handler = new Handler();

        handler.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                checkForDevices();
            }
        }, 1000);
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);
    }

    private static native void notifyDeviceAttached(int fd);
    private static native void notifyDeviceDetached(int fd);

    private final BroadcastReceiver usbManagerBroadcastReceiver = new BroadcastReceiver()
    {
        public void onReceive(Context context, Intent intent)
        {
            try
            {
                String action = intent.getAction();

                Log.d(TAG, "INTENT ACTION: " + action);

                if (ACTION_USB_PERMISSION.equals(action))
                {
                    Log.d(TAG, "onUsbPermission");

                    synchronized (this)
                    {
                        UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
                        {
                            if(device != null)
                            {
                                int fd = connectToDevice(device);
                                Log.d(TAG,"device file descriptor: " + fd);
                                notifyDeviceAttached(fd);
                            }
                        }
                        else
                        {
                            Log.d(TAG, "permission denied for device " + device);
                        }
                    }
                }

                if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action))
                {
                    Log.d(TAG, "onDeviceConnected");

                    synchronized(this)
                    {
                        UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                        if (device != null)
                        {
                            manager.requestPermission(device, mPermissionIntent);
                        }
                    }
                }

                if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action))
                {
                    Log.d(TAG, "onDeviceDisconnected");

                    synchronized(this)
                    {
                        UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                        int fd = connectedDevices.get(device.getDeviceId());

                        Log.d(TAG, "device: " + device.getDeviceId() + " disconnected. fd: " + fd);

                        notifyDeviceDetached(fd);

                        connectedDevices.remove(device.getDeviceId());
                    }
                }
            }
            catch(Exception e)
            {
                Log.d(TAG, "Exception: " + e);
            }
        }
    };

    private int connectToDevice(UsbDevice device)
    {
        connection = manager.openDevice(device);
        // if we make this, kernel driver will be disconnected
        connection.claimInterface(device.getInterface(0), true);

        Log.d(TAG, "inserting device with id: " + device.getDeviceId() + " and file descriptor: " + connection.getFileDescriptor());
        connectedDevices.put(device.getDeviceId(), connection.getFileDescriptor());

        return connection.getFileDescriptor();
    }

    private void checkForDevices()
    {
        HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
        Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();

        while(deviceIterator.hasNext())
        {
            UsbDevice device = deviceIterator.next();

            if (device.getVendorId()==VID && device.getProductId()==PID)
            {
                Log.d(TAG, "Found a device: " + device);

                manager.requestPermission(device, mPermissionIntent);
            }
        }
    }
}

当具有所需 VID 和 PID 的设备连接或断开连接时, native 调用 notifyDeviceAttached(int fd) 和 notifyDeviceDetached(int fd) 被调用,将设备的文件描述符发送到 native 端。在我的例子中,我实例化了一个 Device 类型的类。此时设备已经打开,您可以开始调用它。在 Linux 中,您可以像 libusb 一样进行 ioctl 调用。您可以在下面查看 getFeature 和 setFeature 的代码。如果您还需要其他任何东西,您可以查看 libusb 源代码。

C++ native 端代码:

#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>

#include <QDebug>
#include <QElapsedTimer>

static inline uint16_t cpu_to_le16(const uint16_t x)
{
    union
    {
        uint8_t  b8[2];
        uint16_t b16;
    } _tmp;

    _tmp.b8[1] = (uint8_t) (x >> 8);
    _tmp.b8[0] = (uint8_t) (x & 0xff);

    return _tmp.b16;
}

struct usbdevfs_ctrltransfer
{
    unsigned char bRequestType;
    unsigned char bRequest;
    unsigned short wValue;
    unsigned short wIndex;
    unsigned short wLength;
    unsigned int timeout;
    void *data;
};

Device::Device(int fileDescriptor, QObject *parent) :
    fd(fileDescriptor)
{
}

int Device::getFeature(unsigned char reportId, unsigned char *buffer, int length)
{
    struct usbdevfs_ctrltransfer data;

    data.bRequestType = (0x01 << 5)|0x01|0x80;
    data.bRequest = 0x01;
    data.wValue = cpu_to_le16((3 << 8) | reportId);
    data.wIndex = cpu_to_le16(0);
    data.wLength = cpu_to_le16(length);
    data.data = buffer;
    data.timeout = 1000;

    int res = ioctl(fd, _IOWR('U', 0, struct usbdevfs_ctrltransfer), &data);

    if (res<0)
    {
        qDebug() << "error: " << strerror(errno);
    }

    return res;
}

int Device::setFeature(unsigned char reportId, unsigned char *buffer, int length)
{
    struct usbdevfs_ctrltransfer data;

    data.bRequestType = (0x01 << 5)|0x01|0x00;
    data.bRequest = 0x09;
    data.wValue = cpu_to_le16((3 << 8) | reportId);
    data.wIndex = cpu_to_le16(0);
    data.wLength = cpu_to_le16(length);
    data.data = buffer;
    data.timeout = 1000;

    int res = ioctl(fd, _IOWR('U', 0, struct usbdevfs_ctrltransfer), &data);

    if (res<0)
    {
        qDebug() << "error: " << strerror(errno);
    }

    return res;
}

问候,

努诺桑托斯

关于android - NDK 级别的 Android 低级 USB API,可在 Qt Android 上使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22197425/

相关文章:

android - 要求和内存消耗

qt - Qt 如何在矩形周围绘制边框?

c++ - 如何在 QComboBox 中显示短值并在其下拉列表中显示长值?

android - java.lang.UnsatisfiedLinkError 在某些设备上崩溃

java - 在 android ndk 上运行 pjsip 示例 pjsua 时出错

android - 未定义对myfunction的引用

android - 比较按钮的背景资源

android - uses-permission-sdk-23 和 uses-permission 之间的区别?

Android:同时 BLE 连接的限制

c++ - 将 QDialog 窗口限制为一个实例 [Qt]?