HIDDeviceUSB.java (9017B)
1 package org.libsdl.app; 2 3 import android.hardware.usb.*; 4 import android.os.Build; 5 import android.util.Log; 6 import java.util.Arrays; 7 8 class HIDDeviceUSB implements HIDDevice { 9 10 private static final String TAG = "hidapi"; 11 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; 23 24 public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) { 25 mManager = manager; 26 mDevice = usbDevice; 27 mInterfaceIndex = interface_index; 28 mInterface = mDevice.getInterface(mInterfaceIndex).getId(); 29 mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier()); 30 mRunning = false; 31 } 32 33 public String getIdentifier() { 34 return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex); 35 } 36 37 @Override 38 public int getId() { 39 return mDeviceId; 40 } 41 42 @Override 43 public int getVendorId() { 44 return mDevice.getVendorId(); 45 } 46 47 @Override 48 public int getProductId() { 49 return mDevice.getProductId(); 50 } 51 52 @Override 53 public String getSerialNumber() { 54 String result = null; 55 if (Build.VERSION.SDK_INT >= 21) { 56 try { 57 result = mDevice.getSerialNumber(); 58 } 59 catch (SecurityException exception) { 60 //Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage()); 61 } 62 } 63 if (result == null) { 64 result = ""; 65 } 66 return result; 67 } 68 69 @Override 70 public int getVersion() { 71 return 0; 72 } 73 74 @Override 75 public String getManufacturerName() { 76 String result = null; 77 if (Build.VERSION.SDK_INT >= 21) { 78 result = mDevice.getManufacturerName(); 79 } 80 if (result == null) { 81 result = String.format("%x", getVendorId()); 82 } 83 return result; 84 } 85 86 @Override 87 public String getProductName() { 88 String result = null; 89 if (Build.VERSION.SDK_INT >= 21) { 90 result = mDevice.getProductName(); 91 } 92 if (result == null) { 93 result = String.format("%x", getProductId()); 94 } 95 return result; 96 } 97 98 @Override 99 public UsbDevice getDevice() { 100 return mDevice; 101 } 102 103 public String getDeviceName() { 104 return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")"; 105 } 106 107 @Override 108 public boolean open() { 109 mConnection = mManager.getUSBManager().openDevice(mDevice); 110 if (mConnection == null) { 111 Log.w(TAG, "Unable to open USB device " + getDeviceName()); 112 return false; 113 } 114 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()); 119 close(); 120 return false; 121 } 122 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; 130 } 131 break; 132 case UsbConstants.USB_DIR_OUT: 133 if (mOutputEndpoint == null) { 134 mOutputEndpoint = endpt; 135 } 136 break; 137 } 138 } 139 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()); 143 close(); 144 return false; 145 } 146 147 // Start listening for input 148 mRunning = true; 149 mInputThread = new InputThread(); 150 mInputThread.start(); 151 152 return true; 153 } 154 155 @Override 156 public int sendFeatureReport(byte[] report) { 157 int res = -1; 158 int offset = 0; 159 int length = report.length; 160 boolean skipped_report_id = false; 161 byte report_number = report[0]; 162 163 if (report_number == 0x0) { 164 ++offset; 165 --length; 166 skipped_report_id = true; 167 } 168 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, 173 mInterface, 174 report, offset, length, 175 1000/*timeout millis*/); 176 177 if (res < 0) { 178 Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName()); 179 return -1; 180 } 181 182 if (skipped_report_id) { 183 ++length; 184 } 185 return length; 186 } 187 188 @Override 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()); 193 } 194 return r; 195 } 196 197 @Override 198 public boolean getFeatureReport(byte[] report) { 199 int res = -1; 200 int offset = 0; 201 int length = report.length; 202 boolean skipped_report_id = false; 203 byte report_number = report[0]; 204 205 if (report_number == 0x0) { 206 /* Offset the return buffer by 1, so that the report ID 207 will remain in byte 0. */ 208 ++offset; 209 --length; 210 skipped_report_id = true; 211 } 212 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, 217 mInterface, 218 report, offset, length, 219 1000/*timeout millis*/); 220 221 if (res < 0) { 222 Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName()); 223 return false; 224 } 225 226 if (skipped_report_id) { 227 ++res; 228 ++length; 229 } 230 231 byte[] data; 232 if (res == length) { 233 data = report; 234 } else { 235 data = Arrays.copyOfRange(report, 0, res); 236 } 237 mManager.HIDDeviceFeatureReport(mDeviceId, data); 238 239 return true; 240 } 241 242 @Override 243 public void close() { 244 mRunning = false; 245 if (mInputThread != null) { 246 while (mInputThread.isAlive()) { 247 mInputThread.interrupt(); 248 try { 249 mInputThread.join(); 250 } catch (InterruptedException e) { 251 // Keep trying until we're done 252 } 253 } 254 mInputThread = null; 255 } 256 if (mConnection != null) { 257 UsbInterface iface = mDevice.getInterface(mInterfaceIndex); 258 mConnection.releaseInterface(iface); 259 mConnection.close(); 260 mConnection = null; 261 } 262 } 263 264 @Override 265 public void shutdown() { 266 close(); 267 mManager = null; 268 } 269 270 @Override 271 public void setFrozen(boolean frozen) { 272 mFrozen = frozen; 273 } 274 275 protected class InputThread extends Thread { 276 @Override 277 public void run() { 278 int packetSize = mInputEndpoint.getMaxPacketSize(); 279 byte[] packet = new byte[packetSize]; 280 while (mRunning) { 281 int r; 282 try 283 { 284 r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000); 285 } 286 catch (Exception e) 287 { 288 Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e); 289 break; 290 } 291 if (r < 0) { 292 // Could be a timeout or an I/O error 293 } 294 if (r > 0) { 295 byte[] data; 296 if (r == packetSize) { 297 data = packet; 298 } else { 299 data = Arrays.copyOfRange(packet, 0, r); 300 } 301 302 if (!mFrozen) { 303 mManager.HIDDeviceInputReport(mDeviceId, data); 304 } 305 } 306 } 307 } 308 } 309 }