changed build system for Android from Ant to Gradle
[rocksndiamonds.git] / build-projects / android / app / src / main / java / org / libsdl / app / HIDDeviceUSB.java
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             result = mDevice.getSerialNumber();
57         }
58         if (result == null) {
59             result = "";
60         }
61         return result;
62     }
63
64     @Override
65     public int getVersion() {
66         return 0;
67     }
68
69     @Override
70     public String getManufacturerName() {
71         String result = null;
72         if (Build.VERSION.SDK_INT >= 21) {
73             result = mDevice.getManufacturerName();
74         }
75         if (result == null) {
76             result = String.format("%x", getVendorId());
77         }
78         return result;
79     }
80
81     @Override
82     public String getProductName() {
83         String result = null;
84         if (Build.VERSION.SDK_INT >= 21) {
85             result = mDevice.getProductName();
86         }
87         if (result == null) {
88             result = String.format("%x", getProductId());
89         }
90         return result;
91     }
92
93     @Override
94     public UsbDevice getDevice() {
95         return mDevice;
96     }
97
98     public String getDeviceName() {
99         return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
100     }
101
102     @Override
103     public boolean open() {
104         mConnection = mManager.getUSBManager().openDevice(mDevice);
105         if (mConnection == null) {
106             Log.w(TAG, "Unable to open USB device " + getDeviceName());
107             return false;
108         }
109
110         // Force claim our interface
111         UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
112         if (!mConnection.claimInterface(iface, true)) {
113             Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
114             close();
115             return false;
116         }
117
118         // Find the endpoints
119         for (int j = 0; j < iface.getEndpointCount(); j++) {
120             UsbEndpoint endpt = iface.getEndpoint(j);
121             switch (endpt.getDirection()) {
122             case UsbConstants.USB_DIR_IN:
123                 if (mInputEndpoint == null) {
124                     mInputEndpoint = endpt;
125                 }
126                 break;
127             case UsbConstants.USB_DIR_OUT:
128                 if (mOutputEndpoint == null) {
129                     mOutputEndpoint = endpt;
130                 }
131                 break;
132             }
133         }
134
135         // Make sure the required endpoints were present
136         if (mInputEndpoint == null || mOutputEndpoint == null) {
137             Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
138             close();
139             return false;
140         }
141
142         // Start listening for input
143         mRunning = true;
144         mInputThread = new InputThread();
145         mInputThread.start();
146
147         return true;
148     }
149
150     @Override
151     public int sendFeatureReport(byte[] report) {
152         int res = -1;
153         int offset = 0;
154         int length = report.length;
155         boolean skipped_report_id = false;
156         byte report_number = report[0];
157
158         if (report_number == 0x0) {
159             ++offset;
160             --length;
161             skipped_report_id = true;
162         }
163
164         res = mConnection.controlTransfer(
165             UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
166             0x09/*HID set_report*/,
167             (3/*HID feature*/ << 8) | report_number,
168             mInterface,
169             report, offset, length,
170             1000/*timeout millis*/);
171
172         if (res < 0) {
173             Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
174             return -1;
175         }
176
177         if (skipped_report_id) {
178             ++length;
179         }
180         return length;
181     }
182
183     @Override
184     public int sendOutputReport(byte[] report) {
185         int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
186         if (r != report.length) {
187             Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
188         }
189         return r;
190     }
191
192     @Override
193     public boolean getFeatureReport(byte[] report) {
194         int res = -1;
195         int offset = 0;
196         int length = report.length;
197         boolean skipped_report_id = false;
198         byte report_number = report[0];
199
200         if (report_number == 0x0) {
201             /* Offset the return buffer by 1, so that the report ID
202                will remain in byte 0. */
203             ++offset;
204             --length;
205             skipped_report_id = true;
206         }
207
208         res = mConnection.controlTransfer(
209             UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
210             0x01/*HID get_report*/,
211             (3/*HID feature*/ << 8) | report_number,
212             mInterface,
213             report, offset, length,
214             1000/*timeout millis*/);
215
216         if (res < 0) {
217             Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
218             return false;
219         }
220
221         if (skipped_report_id) {
222             ++res;
223             ++length;
224         }
225
226         byte[] data;
227         if (res == length) {
228             data = report;
229         } else {
230             data = Arrays.copyOfRange(report, 0, res);
231         }
232         mManager.HIDDeviceFeatureReport(mDeviceId, data);
233
234         return true;
235     }
236
237     @Override
238     public void close() {
239         mRunning = false;
240         if (mInputThread != null) {
241             while (mInputThread.isAlive()) {
242                 mInputThread.interrupt();
243                 try {
244                     mInputThread.join();
245                 } catch (InterruptedException e) {
246                     // Keep trying until we're done
247                 }
248             }
249             mInputThread = null;
250         }
251         if (mConnection != null) {
252             UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
253             mConnection.releaseInterface(iface);
254             mConnection.close();
255             mConnection = null;
256         }
257     }
258
259     @Override
260     public void shutdown() {
261         close();
262         mManager = null;
263     }
264
265     @Override
266     public void setFrozen(boolean frozen) {
267         mFrozen = frozen;
268     }
269
270     protected class InputThread extends Thread {
271         @Override
272         public void run() {
273             int packetSize = mInputEndpoint.getMaxPacketSize();
274             byte[] packet = new byte[packetSize];
275             while (mRunning) {
276                 int r;
277                 try
278                 {
279                     r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
280                 }
281                 catch (Exception e)
282                 {
283                     Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
284                     break;
285                 }
286                 if (r < 0) {
287                     // Could be a timeout or an I/O error
288                 }
289                 if (r > 0) {
290                     byte[] data;
291                     if (r == packetSize) {
292                         data = packet;
293                     } else {
294                         data = Arrays.copyOfRange(packet, 0, r);
295                     }
296
297                     if (!mFrozen) {
298                         mManager.HIDDeviceInputReport(mDeviceId, data);
299                     }
300                 }
301             }
302         }
303     }
304 }