HIDDeviceManager.java 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. package org.libsdl.app;
  2. import android.app.Activity;
  3. import android.app.AlertDialog;
  4. import android.app.PendingIntent;
  5. import android.bluetooth.BluetoothAdapter;
  6. import android.bluetooth.BluetoothDevice;
  7. import android.bluetooth.BluetoothManager;
  8. import android.bluetooth.BluetoothProfile;
  9. import android.os.Build;
  10. import android.util.Log;
  11. import android.content.BroadcastReceiver;
  12. import android.content.Context;
  13. import android.content.DialogInterface;
  14. import android.content.Intent;
  15. import android.content.IntentFilter;
  16. import android.content.SharedPreferences;
  17. import android.content.pm.PackageManager;
  18. import android.hardware.usb.*;
  19. import android.os.Handler;
  20. import android.os.Looper;
  21. import java.util.ArrayList;
  22. import java.util.HashMap;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. public class HIDDeviceManager {
  26. private static final String TAG = "hidapi";
  27. private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
  28. private static HIDDeviceManager sManager;
  29. private static int sManagerRefCount = 0;
  30. public static HIDDeviceManager acquire(Context context) {
  31. if (sManagerRefCount == 0) {
  32. sManager = new HIDDeviceManager(context);
  33. }
  34. ++sManagerRefCount;
  35. return sManager;
  36. }
  37. public static void release(HIDDeviceManager manager) {
  38. if (manager == sManager) {
  39. --sManagerRefCount;
  40. if (sManagerRefCount == 0) {
  41. sManager.close();
  42. sManager = null;
  43. }
  44. }
  45. }
  46. private Context mContext;
  47. private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
  48. private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
  49. private int mNextDeviceId = 0;
  50. private SharedPreferences mSharedPreferences = null;
  51. private boolean mIsChromebook = false;
  52. private UsbManager mUsbManager;
  53. private Handler mHandler;
  54. private BluetoothManager mBluetoothManager;
  55. private List<BluetoothDevice> mLastBluetoothDevices;
  56. private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
  57. @Override
  58. public void onReceive(Context context, Intent intent) {
  59. String action = intent.getAction();
  60. if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
  61. UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
  62. handleUsbDeviceAttached(usbDevice);
  63. } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
  64. UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
  65. handleUsbDeviceDetached(usbDevice);
  66. } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
  67. UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
  68. handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
  69. }
  70. }
  71. };
  72. private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
  73. @Override
  74. public void onReceive(Context context, Intent intent) {
  75. String action = intent.getAction();
  76. // Bluetooth device was connected. If it was a Steam Controller, handle it
  77. if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
  78. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  79. Log.d(TAG, "Bluetooth device connected: " + device);
  80. if (isSteamController(device)) {
  81. connectBluetoothDevice(device);
  82. }
  83. }
  84. // Bluetooth device was disconnected, remove from controller manager (if any)
  85. if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
  86. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  87. Log.d(TAG, "Bluetooth device disconnected: " + device);
  88. disconnectBluetoothDevice(device);
  89. }
  90. }
  91. };
  92. private HIDDeviceManager(final Context context) {
  93. mContext = context;
  94. HIDDeviceRegisterCallback();
  95. mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
  96. mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
  97. // if (shouldClear) {
  98. // SharedPreferences.Editor spedit = mSharedPreferences.edit();
  99. // spedit.clear();
  100. // spedit.commit();
  101. // }
  102. // else
  103. {
  104. mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
  105. }
  106. }
  107. public Context getContext() {
  108. return mContext;
  109. }
  110. public int getDeviceIDForIdentifier(String identifier) {
  111. SharedPreferences.Editor spedit = mSharedPreferences.edit();
  112. int result = mSharedPreferences.getInt(identifier, 0);
  113. if (result == 0) {
  114. result = mNextDeviceId++;
  115. spedit.putInt("next_device_id", mNextDeviceId);
  116. }
  117. spedit.putInt(identifier, result);
  118. spedit.commit();
  119. return result;
  120. }
  121. private void initializeUSB() {
  122. mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
  123. if (mUsbManager == null) {
  124. return;
  125. }
  126. /*
  127. // Logging
  128. for (UsbDevice device : mUsbManager.getDeviceList().values()) {
  129. Log.i(TAG,"Path: " + device.getDeviceName());
  130. Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
  131. Log.i(TAG,"Product: " + device.getProductName());
  132. Log.i(TAG,"ID: " + device.getDeviceId());
  133. Log.i(TAG,"Class: " + device.getDeviceClass());
  134. Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
  135. Log.i(TAG,"Vendor ID " + device.getVendorId());
  136. Log.i(TAG,"Product ID: " + device.getProductId());
  137. Log.i(TAG,"Interface count: " + device.getInterfaceCount());
  138. Log.i(TAG,"---------------------------------------");
  139. // Get interface details
  140. for (int index = 0; index < device.getInterfaceCount(); index++) {
  141. UsbInterface mUsbInterface = device.getInterface(index);
  142. Log.i(TAG," ***** *****");
  143. Log.i(TAG," Interface index: " + index);
  144. Log.i(TAG," Interface ID: " + mUsbInterface.getId());
  145. Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
  146. Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
  147. Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
  148. Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
  149. // Get endpoint details
  150. for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
  151. {
  152. UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
  153. Log.i(TAG," ++++ ++++ ++++");
  154. Log.i(TAG," Endpoint index: " + epi);
  155. Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
  156. Log.i(TAG," Direction: " + mEndpoint.getDirection());
  157. Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
  158. Log.i(TAG," Interval: " + mEndpoint.getInterval());
  159. Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
  160. Log.i(TAG," Type: " + mEndpoint.getType());
  161. }
  162. }
  163. }
  164. Log.i(TAG," No more devices connected.");
  165. */
  166. // Register for USB broadcasts and permission completions
  167. IntentFilter filter = new IntentFilter();
  168. filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
  169. filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
  170. filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
  171. mContext.registerReceiver(mUsbBroadcast, filter);
  172. for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
  173. handleUsbDeviceAttached(usbDevice);
  174. }
  175. }
  176. UsbManager getUSBManager() {
  177. return mUsbManager;
  178. }
  179. private void shutdownUSB() {
  180. try {
  181. mContext.unregisterReceiver(mUsbBroadcast);
  182. } catch (Exception e) {
  183. // We may not have registered, that's okay
  184. }
  185. }
  186. private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
  187. if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
  188. return true;
  189. }
  190. if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
  191. return true;
  192. }
  193. return false;
  194. }
  195. private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
  196. final int XB360_IFACE_SUBCLASS = 93;
  197. final int XB360_IFACE_PROTOCOL = 1; // Wired
  198. final int XB360W_IFACE_PROTOCOL = 129; // Wireless
  199. final int[] SUPPORTED_VENDORS = {
  200. 0x0079, // GPD Win 2
  201. 0x044f, // Thrustmaster
  202. 0x045e, // Microsoft
  203. 0x046d, // Logitech
  204. 0x056e, // Elecom
  205. 0x06a3, // Saitek
  206. 0x0738, // Mad Catz
  207. 0x07ff, // Mad Catz
  208. 0x0e6f, // PDP
  209. 0x0f0d, // Hori
  210. 0x1038, // SteelSeries
  211. 0x11c9, // Nacon
  212. 0x12ab, // Unknown
  213. 0x1430, // RedOctane
  214. 0x146b, // BigBen
  215. 0x1532, // Razer Sabertooth
  216. 0x15e4, // Numark
  217. 0x162e, // Joytech
  218. 0x1689, // Razer Onza
  219. 0x1949, // Lab126, Inc.
  220. 0x1bad, // Harmonix
  221. 0x20d6, // PowerA
  222. 0x24c6, // PowerA
  223. 0x2c22, // Qanba
  224. 0x2dc8, // 8BitDo
  225. 0x9886, // ASTRO Gaming
  226. };
  227. if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
  228. usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
  229. (usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
  230. usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
  231. int vendor_id = usbDevice.getVendorId();
  232. for (int supportedVid : SUPPORTED_VENDORS) {
  233. if (vendor_id == supportedVid) {
  234. return true;
  235. }
  236. }
  237. }
  238. return false;
  239. }
  240. private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
  241. final int XB1_IFACE_SUBCLASS = 71;
  242. final int XB1_IFACE_PROTOCOL = 208;
  243. final int[] SUPPORTED_VENDORS = {
  244. 0x03f0, // HP
  245. 0x044f, // Thrustmaster
  246. 0x045e, // Microsoft
  247. 0x0738, // Mad Catz
  248. 0x0e6f, // PDP
  249. 0x0f0d, // Hori
  250. 0x10f5, // Turtle Beach
  251. 0x1532, // Razer Wildcat
  252. 0x20d6, // PowerA
  253. 0x24c6, // PowerA
  254. 0x2dc8, // 8BitDo
  255. 0x2e24, // Hyperkin
  256. };
  257. if (usbInterface.getId() == 0 &&
  258. usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
  259. usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
  260. usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
  261. int vendor_id = usbDevice.getVendorId();
  262. for (int supportedVid : SUPPORTED_VENDORS) {
  263. if (vendor_id == supportedVid) {
  264. return true;
  265. }
  266. }
  267. }
  268. return false;
  269. }
  270. private void handleUsbDeviceAttached(UsbDevice usbDevice) {
  271. connectHIDDeviceUSB(usbDevice);
  272. }
  273. private void handleUsbDeviceDetached(UsbDevice usbDevice) {
  274. List<Integer> devices = new ArrayList<Integer>();
  275. for (HIDDevice device : mDevicesById.values()) {
  276. if (usbDevice.equals(device.getDevice())) {
  277. devices.add(device.getId());
  278. }
  279. }
  280. for (int id : devices) {
  281. HIDDevice device = mDevicesById.get(id);
  282. mDevicesById.remove(id);
  283. device.shutdown();
  284. HIDDeviceDisconnected(id);
  285. }
  286. }
  287. private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
  288. for (HIDDevice device : mDevicesById.values()) {
  289. if (usbDevice.equals(device.getDevice())) {
  290. boolean opened = false;
  291. if (permission_granted) {
  292. opened = device.open();
  293. }
  294. HIDDeviceOpenResult(device.getId(), opened);
  295. }
  296. }
  297. }
  298. private void connectHIDDeviceUSB(UsbDevice usbDevice) {
  299. synchronized (this) {
  300. int interface_mask = 0;
  301. for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
  302. UsbInterface usbInterface = usbDevice.getInterface(interface_index);
  303. if (isHIDDeviceInterface(usbDevice, usbInterface)) {
  304. // Check to see if we've already added this interface
  305. // This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
  306. int interface_id = usbInterface.getId();
  307. if ((interface_mask & (1 << interface_id)) != 0) {
  308. continue;
  309. }
  310. interface_mask |= (1 << interface_id);
  311. HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
  312. int id = device.getId();
  313. mDevicesById.put(id, device);
  314. HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
  315. }
  316. }
  317. }
  318. }
  319. private void initializeBluetooth() {
  320. Log.d(TAG, "Initializing Bluetooth");
  321. if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
  322. mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
  323. Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
  324. return;
  325. }
  326. if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
  327. Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
  328. return;
  329. }
  330. // Find bonded bluetooth controllers and create SteamControllers for them
  331. mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
  332. if (mBluetoothManager == null) {
  333. // This device doesn't support Bluetooth.
  334. return;
  335. }
  336. BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
  337. if (btAdapter == null) {
  338. // This device has Bluetooth support in the codebase, but has no available adapters.
  339. return;
  340. }
  341. // Get our bonded devices.
  342. for (BluetoothDevice device : btAdapter.getBondedDevices()) {
  343. Log.d(TAG, "Bluetooth device available: " + device);
  344. if (isSteamController(device)) {
  345. connectBluetoothDevice(device);
  346. }
  347. }
  348. // NOTE: These don't work on Chromebooks, to my undying dismay.
  349. IntentFilter filter = new IntentFilter();
  350. filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
  351. filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
  352. mContext.registerReceiver(mBluetoothBroadcast, filter);
  353. if (mIsChromebook) {
  354. mHandler = new Handler(Looper.getMainLooper());
  355. mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
  356. // final HIDDeviceManager finalThis = this;
  357. // mHandler.postDelayed(new Runnable() {
  358. // @Override
  359. // public void run() {
  360. // finalThis.chromebookConnectionHandler();
  361. // }
  362. // }, 5000);
  363. }
  364. }
  365. private void shutdownBluetooth() {
  366. try {
  367. mContext.unregisterReceiver(mBluetoothBroadcast);
  368. } catch (Exception e) {
  369. // We may not have registered, that's okay
  370. }
  371. }
  372. // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
  373. // This function provides a sort of dummy version of that, watching for changes in the
  374. // connected devices and attempting to add controllers as things change.
  375. public void chromebookConnectionHandler() {
  376. if (!mIsChromebook) {
  377. return;
  378. }
  379. ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
  380. ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
  381. List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
  382. for (BluetoothDevice bluetoothDevice : currentConnected) {
  383. if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
  384. connected.add(bluetoothDevice);
  385. }
  386. }
  387. for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
  388. if (!currentConnected.contains(bluetoothDevice)) {
  389. disconnected.add(bluetoothDevice);
  390. }
  391. }
  392. mLastBluetoothDevices = currentConnected;
  393. for (BluetoothDevice bluetoothDevice : disconnected) {
  394. disconnectBluetoothDevice(bluetoothDevice);
  395. }
  396. for (BluetoothDevice bluetoothDevice : connected) {
  397. connectBluetoothDevice(bluetoothDevice);
  398. }
  399. final HIDDeviceManager finalThis = this;
  400. mHandler.postDelayed(new Runnable() {
  401. @Override
  402. public void run() {
  403. finalThis.chromebookConnectionHandler();
  404. }
  405. }, 10000);
  406. }
  407. public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
  408. Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
  409. synchronized (this) {
  410. if (mBluetoothDevices.containsKey(bluetoothDevice)) {
  411. Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
  412. HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
  413. device.reconnect();
  414. return false;
  415. }
  416. HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
  417. int id = device.getId();
  418. mBluetoothDevices.put(bluetoothDevice, device);
  419. mDevicesById.put(id, device);
  420. // The Steam Controller will mark itself connected once initialization is complete
  421. }
  422. return true;
  423. }
  424. public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
  425. synchronized (this) {
  426. HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
  427. if (device == null)
  428. return;
  429. int id = device.getId();
  430. mBluetoothDevices.remove(bluetoothDevice);
  431. mDevicesById.remove(id);
  432. device.shutdown();
  433. HIDDeviceDisconnected(id);
  434. }
  435. }
  436. public boolean isSteamController(BluetoothDevice bluetoothDevice) {
  437. // Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
  438. if (bluetoothDevice == null) {
  439. return false;
  440. }
  441. // If the device has no local name, we really don't want to try an equality check against it.
  442. if (bluetoothDevice.getName() == null) {
  443. return false;
  444. }
  445. return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
  446. }
  447. private void close() {
  448. shutdownUSB();
  449. shutdownBluetooth();
  450. synchronized (this) {
  451. for (HIDDevice device : mDevicesById.values()) {
  452. device.shutdown();
  453. }
  454. mDevicesById.clear();
  455. mBluetoothDevices.clear();
  456. HIDDeviceReleaseCallback();
  457. }
  458. }
  459. public void setFrozen(boolean frozen) {
  460. synchronized (this) {
  461. for (HIDDevice device : mDevicesById.values()) {
  462. device.setFrozen(frozen);
  463. }
  464. }
  465. }
  466. //////////////////////////////////////////////////////////////////////////////////////////////////////
  467. //////////////////////////////////////////////////////////////////////////////////////////////////////
  468. //////////////////////////////////////////////////////////////////////////////////////////////////////
  469. private HIDDevice getDevice(int id) {
  470. synchronized (this) {
  471. HIDDevice result = mDevicesById.get(id);
  472. if (result == null) {
  473. Log.v(TAG, "No device for id: " + id);
  474. Log.v(TAG, "Available devices: " + mDevicesById.keySet());
  475. }
  476. return result;
  477. }
  478. }
  479. //////////////////////////////////////////////////////////////////////////////////////////////////////
  480. ////////// JNI interface functions
  481. //////////////////////////////////////////////////////////////////////////////////////////////////////
  482. public boolean initialize(boolean usb, boolean bluetooth) {
  483. Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
  484. if (usb) {
  485. initializeUSB();
  486. }
  487. if (bluetooth) {
  488. initializeBluetooth();
  489. }
  490. return true;
  491. }
  492. public boolean openDevice(int deviceID) {
  493. Log.v(TAG, "openDevice deviceID=" + deviceID);
  494. HIDDevice device = getDevice(deviceID);
  495. if (device == null) {
  496. HIDDeviceDisconnected(deviceID);
  497. return false;
  498. }
  499. // Look to see if this is a USB device and we have permission to access it
  500. UsbDevice usbDevice = device.getDevice();
  501. if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
  502. HIDDeviceOpenPending(deviceID);
  503. try {
  504. final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
  505. int flags;
  506. if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
  507. flags = FLAG_MUTABLE;
  508. } else {
  509. flags = 0;
  510. }
  511. mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
  512. } catch (Exception e) {
  513. Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
  514. HIDDeviceOpenResult(deviceID, false);
  515. }
  516. return false;
  517. }
  518. try {
  519. return device.open();
  520. } catch (Exception e) {
  521. Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
  522. }
  523. return false;
  524. }
  525. public int sendOutputReport(int deviceID, byte[] report) {
  526. try {
  527. //Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
  528. HIDDevice device;
  529. device = getDevice(deviceID);
  530. if (device == null) {
  531. HIDDeviceDisconnected(deviceID);
  532. return -1;
  533. }
  534. return device.sendOutputReport(report);
  535. } catch (Exception e) {
  536. Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
  537. }
  538. return -1;
  539. }
  540. public int sendFeatureReport(int deviceID, byte[] report) {
  541. try {
  542. //Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
  543. HIDDevice device;
  544. device = getDevice(deviceID);
  545. if (device == null) {
  546. HIDDeviceDisconnected(deviceID);
  547. return -1;
  548. }
  549. return device.sendFeatureReport(report);
  550. } catch (Exception e) {
  551. Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
  552. }
  553. return -1;
  554. }
  555. public boolean getFeatureReport(int deviceID, byte[] report) {
  556. try {
  557. //Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
  558. HIDDevice device;
  559. device = getDevice(deviceID);
  560. if (device == null) {
  561. HIDDeviceDisconnected(deviceID);
  562. return false;
  563. }
  564. return device.getFeatureReport(report);
  565. } catch (Exception e) {
  566. Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
  567. }
  568. return false;
  569. }
  570. public void closeDevice(int deviceID) {
  571. try {
  572. Log.v(TAG, "closeDevice deviceID=" + deviceID);
  573. HIDDevice device;
  574. device = getDevice(deviceID);
  575. if (device == null) {
  576. HIDDeviceDisconnected(deviceID);
  577. return;
  578. }
  579. device.close();
  580. } catch (Exception e) {
  581. Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
  582. }
  583. }
  584. //////////////////////////////////////////////////////////////////////////////////////////////////////
  585. /////////////// Native methods
  586. //////////////////////////////////////////////////////////////////////////////////////////////////////
  587. private native void HIDDeviceRegisterCallback();
  588. private native void HIDDeviceReleaseCallback();
  589. native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
  590. native void HIDDeviceOpenPending(int deviceID);
  591. native void HIDDeviceOpenResult(int deviceID, boolean opened);
  592. native void HIDDeviceDisconnected(int deviceID);
  593. native void HIDDeviceInputReport(int deviceID, byte[] report);
  594. native void HIDDeviceFeatureReport(int deviceID, byte[] report);
  595. }