1 package org.libsdl.app;
3 import android.hardware.usb.*;
4 import android.os.Build;
5 import android.util.Log;
6 import java.util.Arrays;
8 class HIDDeviceUSB implements HIDDevice {
10 private static final String TAG = "hidapi";
12 protected HIDDeviceManager mManager;
13 protected UsbDevice mDevice;
14 protected int mInterfaceIndex;
15 protected int mInterface;
16 protected int mDeviceId;
17 protected UsbDeviceConnection mConnection;
18 protected UsbEndpoint mInputEndpoint;
19 protected UsbEndpoint mOutputEndpoint;
20 protected InputThread mInputThread;
21 protected boolean mRunning;
22 protected boolean mFrozen;
24 public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
27 mInterfaceIndex = interface_index;
28 mInterface = mDevice.getInterface(mInterfaceIndex).getId();
29 mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
33 public String getIdentifier() {
34 return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
43 public int getVendorId() {
44 return mDevice.getVendorId();
48 public int getProductId() {
49 return mDevice.getProductId();
53 public String getSerialNumber() {
55 if (Build.VERSION.SDK_INT >= 21) {
57 result = mDevice.getSerialNumber();
59 catch (SecurityException exception) {
60 //Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
70 public int getVersion() {
75 public String getManufacturerName() {
77 if (Build.VERSION.SDK_INT >= 21) {
78 result = mDevice.getManufacturerName();
81 result = String.format("%x", getVendorId());
87 public String getProductName() {
89 if (Build.VERSION.SDK_INT >= 21) {
90 result = mDevice.getProductName();
93 result = String.format("%x", getProductId());
99 public UsbDevice getDevice() {
103 public String getDeviceName() {
104 return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
108 public boolean open() {
109 mConnection = mManager.getUSBManager().openDevice(mDevice);
110 if (mConnection == null) {
111 Log.w(TAG, "Unable to open USB device " + getDeviceName());
115 // Force claim our interface
116 UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
117 if (!mConnection.claimInterface(iface, true)) {
118 Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
123 // Find the endpoints
124 for (int j = 0; j < iface.getEndpointCount(); j++) {
125 UsbEndpoint endpt = iface.getEndpoint(j);
126 switch (endpt.getDirection()) {
127 case UsbConstants.USB_DIR_IN:
128 if (mInputEndpoint == null) {
129 mInputEndpoint = endpt;
132 case UsbConstants.USB_DIR_OUT:
133 if (mOutputEndpoint == null) {
134 mOutputEndpoint = endpt;
140 // Make sure the required endpoints were present
141 if (mInputEndpoint == null || mOutputEndpoint == null) {
142 Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
147 // Start listening for input
149 mInputThread = new InputThread();
150 mInputThread.start();
156 public int sendFeatureReport(byte[] report) {
159 int length = report.length;
160 boolean skipped_report_id = false;
161 byte report_number = report[0];
163 if (report_number == 0x0) {
166 skipped_report_id = true;
169 res = mConnection.controlTransfer(
170 UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
171 0x09/*HID set_report*/,
172 (3/*HID feature*/ << 8) | report_number,
174 report, offset, length,
175 1000/*timeout millis*/);
178 Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
182 if (skipped_report_id) {
189 public int sendOutputReport(byte[] report) {
190 int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
191 if (r != report.length) {
192 Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
198 public boolean getFeatureReport(byte[] report) {
201 int length = report.length;
202 boolean skipped_report_id = false;
203 byte report_number = report[0];
205 if (report_number == 0x0) {
206 /* Offset the return buffer by 1, so that the report ID
207 will remain in byte 0. */
210 skipped_report_id = true;
213 res = mConnection.controlTransfer(
214 UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
215 0x01/*HID get_report*/,
216 (3/*HID feature*/ << 8) | report_number,
218 report, offset, length,
219 1000/*timeout millis*/);
222 Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
226 if (skipped_report_id) {
235 data = Arrays.copyOfRange(report, 0, res);
237 mManager.HIDDeviceFeatureReport(mDeviceId, data);
243 public void close() {
245 if (mInputThread != null) {
246 while (mInputThread.isAlive()) {
247 mInputThread.interrupt();
250 } catch (InterruptedException e) {
251 // Keep trying until we're done
256 if (mConnection != null) {
257 UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
258 mConnection.releaseInterface(iface);
265 public void shutdown() {
271 public void setFrozen(boolean frozen) {
275 protected class InputThread extends Thread {
278 int packetSize = mInputEndpoint.getMaxPacketSize();
279 byte[] packet = new byte[packetSize];
284 r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
288 Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
292 // Could be a timeout or an I/O error
296 if (r == packetSize) {
299 data = Arrays.copyOfRange(packet, 0, r);
303 mManager.HIDDeviceInputReport(mDeviceId, data);