1 package org.libsdl.app;
3 import android.app.Activity;
4 import android.app.AlertDialog;
5 import android.app.PendingIntent;
6 import android.bluetooth.BluetoothAdapter;
7 import android.bluetooth.BluetoothDevice;
8 import android.bluetooth.BluetoothManager;
9 import android.bluetooth.BluetoothProfile;
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;
22 import java.util.HashMap;
23 import java.util.ArrayList;
24 import java.util.List;
27 private static final String
TAG =
"hidapi";
34 if (sManagerRefCount == 0) {
42 if (manager == sManager) {
44 if (sManagerRefCount == 0) {
52 private HashMap<Integer, HIDDevice>
mDevicesById =
new HashMap<Integer, HIDDevice>();
53 private HashMap<UsbDevice, HIDDeviceUSB>
mUSBDevices =
new HashMap<UsbDevice, HIDDeviceUSB>();
54 private HashMap<BluetoothDevice, HIDDeviceBLESteamController>
mBluetoothDevices =
new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
63 private final BroadcastReceiver
mUsbBroadcast =
new BroadcastReceiver() {
65 public void onReceive(Context
context, Intent intent) {
66 String action = intent.getAction();
67 if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
68 UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
70 }
else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
71 UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
74 UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
82 public void onReceive(Context
context, Intent intent) {
83 String action = intent.getAction();
85 if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
86 BluetoothDevice
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
87 Log.d(TAG,
"Bluetooth device connected: " + device);
95 if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
96 BluetoothDevice
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
97 Log.d(TAG,
"Bluetooth device disconnected: " + device);
110 }
catch (Throwable
e) {
111 Log.w(TAG,
"Couldn't load hidapi: " + e.toString());
113 AlertDialog.Builder builder =
new AlertDialog.Builder(context);
114 builder.setCancelable(
false);
115 builder.setTitle(
"SDL HIDAPI Error");
116 builder.setMessage(
"Please report the following error to the SDL maintainers: " + e.getMessage());
117 builder.setNegativeButton(
"Quit",
new DialogInterface.OnClickListener() {
119 public void onClick(DialogInterface dialog,
int which) {
123 Activity activity = (Activity)context;
127 catch (ClassCastException cce) {
139 mSharedPreferences = mContext.getSharedPreferences(
"hidapi", Context.MODE_PRIVATE);
140 mIsChromebook = mContext.getPackageManager().hasSystemFeature(
"org.chromium.arc.device_management");
149 mNextDeviceId = mSharedPreferences.getInt(
"next_device_id", 0);
161 SharedPreferences.Editor spedit = mSharedPreferences.edit();
163 int result = mSharedPreferences.getInt(identifier, 0);
165 result = mNextDeviceId++;
166 spedit.putInt(
"next_device_id", mNextDeviceId);
169 spedit.putInt(identifier, result);
175 mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
221 IntentFilter
filter =
new IntentFilter();
222 filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
223 filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
225 mContext.registerReceiver(mUsbBroadcast, filter);
227 for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
232 UsbManager getUSBManager() {
238 mContext.unregisterReceiver(mUsbBroadcast);
239 }
catch (Exception
e) {
245 for (
int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); ++interface_number) {
254 UsbInterface usbInterface = usbDevice.getInterface(interface_number);
255 if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
258 if (interface_number == 0) {
267 final int XB360_IFACE_SUBCLASS = 93;
268 final int XB360_IFACE_PROTOCOL = 1;
269 final int[] SUPPORTED_VENDORS = {
292 if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
293 usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
294 usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL) {
295 int vendor_id = usbDevice.getVendorId();
296 for (
int supportedVid : SUPPORTED_VENDORS) {
297 if (vendor_id == supportedVid) {
306 final int XB1_IFACE_SUBCLASS = 71;
307 final int XB1_IFACE_PROTOCOL = 208;
308 final int[] SUPPORTED_VENDORS = {
317 if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
318 usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
319 usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
320 int vendor_id = usbDevice.getVendorId();
321 for (
int supportedVid : SUPPORTED_VENDORS) {
322 if (vendor_id == supportedVid) {
337 HIDDeviceUSB
device = mUSBDevices.get(usbDevice);
341 int id = device.getId();
342 mUSBDevices.remove(usbDevice);
343 mDevicesById.remove(
id);
345 HIDDeviceDisconnected(
id);
349 HIDDeviceUSB
device = mUSBDevices.get(usbDevice);
353 boolean opened =
false;
354 if (permission_granted) {
355 opened = device.open();
357 HIDDeviceOpenResult(device.getId(), opened);
361 synchronized (
this) {
362 for (
int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); interface_number++) {
364 HIDDeviceUSB
device =
new HIDDeviceUSB(
this, usbDevice, interface_number);
365 int id = device.getId();
366 mUSBDevices.put(usbDevice, device);
367 mDevicesById.put(
id, device);
368 HIDDeviceConnected(
id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), interface_number);
376 Log.d(TAG,
"Initializing Bluetooth");
378 if (mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
379 Log.d(TAG,
"Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
384 mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
385 if (mBluetoothManager == null) {
390 BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
391 if (btAdapter == null) {
397 for (BluetoothDevice
device : btAdapter.getBondedDevices()) {
399 Log.d(TAG,
"Bluetooth device available: " +
device);
407 IntentFilter
filter =
new IntentFilter();
408 filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
409 filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
410 mContext.registerReceiver(mBluetoothBroadcast, filter);
413 mHandler =
new Handler(Looper.getMainLooper());
414 mLastBluetoothDevices =
new ArrayList<>();
428 mContext.unregisterReceiver(mBluetoothBroadcast);
429 }
catch (Exception
e) {
438 if (!mIsChromebook) {
442 ArrayList<BluetoothDevice> disconnected =
new ArrayList<>();
443 ArrayList<BluetoothDevice> connected =
new ArrayList<>();
445 List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
447 for (BluetoothDevice bluetoothDevice : currentConnected) {
448 if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
449 connected.add(bluetoothDevice);
452 for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
453 if (!currentConnected.contains(bluetoothDevice)) {
454 disconnected.add(bluetoothDevice);
458 mLastBluetoothDevices = currentConnected;
460 for (BluetoothDevice bluetoothDevice : disconnected) {
463 for (BluetoothDevice bluetoothDevice : connected) {
468 mHandler.postDelayed(
new Runnable() {
477 Log.v(TAG,
"connectBluetoothDevice device=" + bluetoothDevice);
478 synchronized (
this) {
479 if (mBluetoothDevices.containsKey(bluetoothDevice)) {
480 Log.v(TAG,
"Steam controller with address " + bluetoothDevice +
" already exists, attempting reconnect");
482 HIDDeviceBLESteamController
device = mBluetoothDevices.get(bluetoothDevice);
487 HIDDeviceBLESteamController
device =
new HIDDeviceBLESteamController(
this, bluetoothDevice);
488 int id = device.getId();
489 mBluetoothDevices.put(bluetoothDevice, device);
490 mDevicesById.put(
id, device);
498 synchronized (
this) {
499 HIDDeviceBLESteamController
device = mBluetoothDevices.get(bluetoothDevice);
503 int id = device.getId();
504 mBluetoothDevices.remove(bluetoothDevice);
505 mDevicesById.remove(
id);
507 HIDDeviceDisconnected(
id);
513 if (bluetoothDevice == null) {
518 if (bluetoothDevice.getName() == null) {
522 return bluetoothDevice.getName().equals(
"SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
528 synchronized (
this) {
529 for (HIDDevice
device : mDevicesById.values()) {
532 mDevicesById.clear();
533 mBluetoothDevices.clear();
539 synchronized (
this) {
540 for (HIDDevice
device : mDevicesById.values()) {
551 synchronized (
this) {
552 HIDDevice
result = mDevicesById.get(
id);
553 if (result == null) {
554 Log.v(TAG,
"No device for id: " +
id);
555 Log.v(TAG,
"Available devices: " + mDevicesById.keySet());
567 for (HIDDeviceUSB
device : mUSBDevices.values()) {
568 if (deviceID ==
device.getId()) {
569 UsbDevice usbDevice =
device.getDevice();
570 if (!mUsbManager.hasPermission(usbDevice)) {
571 HIDDeviceOpenPending(deviceID);
574 }
catch (Exception
e) {
575 Log.v(TAG,
"Couldn't request permission for USB device " + usbDevice);
576 HIDDeviceOpenResult(deviceID,
false);
585 Log.v(TAG,
"openDevice deviceID=" + deviceID);
588 if (device == null) {
589 HIDDeviceDisconnected(deviceID);
593 return device.open();
594 }
catch (Exception
e) {
595 Log.e(TAG,
"Got exception: " + Log.getStackTraceString(e));
602 Log.v(TAG,
"sendOutputReport deviceID=" + deviceID +
" length=" + report.length);
605 if (device == null) {
606 HIDDeviceDisconnected(deviceID);
610 return device.sendOutputReport(report);
611 }
catch (Exception
e) {
612 Log.e(TAG,
"Got exception: " + Log.getStackTraceString(e));
619 Log.v(TAG,
"sendFeatureReport deviceID=" + deviceID +
" length=" + report.length);
622 if (device == null) {
623 HIDDeviceDisconnected(deviceID);
627 return device.sendFeatureReport(report);
628 }
catch (Exception
e) {
629 Log.e(TAG,
"Got exception: " + Log.getStackTraceString(e));
636 Log.v(TAG,
"getFeatureReport deviceID=" + deviceID);
639 if (device == null) {
640 HIDDeviceDisconnected(deviceID);
644 return device.getFeatureReport(report);
645 }
catch (Exception
e) {
646 Log.e(TAG,
"Got exception: " + Log.getStackTraceString(e));
653 Log.v(TAG,
"closeDevice deviceID=" + deviceID);
656 if (device == null) {
657 HIDDeviceDisconnected(deviceID);
662 }
catch (Exception
e) {
663 Log.e(TAG,
"Got exception: " + Log.getStackTraceString(e));
675 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);
676 native
void HIDDeviceOpenPending(
int deviceID);
677 native
void HIDDeviceOpenResult(
int deviceID,
boolean opened);
678 native
void HIDDeviceDisconnected(
int deviceID);
680 native
void HIDDeviceInputReport(
int deviceID, byte[] report);
681 native
void HIDDeviceFeatureReport(
int deviceID, byte[] report);
native void HIDDeviceReleaseCallback()
int sendFeatureReport(int deviceID, byte[] report)
void handleUsbDeviceAttached(UsbDevice usbDevice)
void connectHIDDeviceUSB(UsbDevice usbDevice)
boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface)
HIDDevice getDevice(int id)
HashMap< Integer, HIDDevice > mDevicesById
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 SDL_AssertionHandler void SDL_SpinLock SDL_atomic_t int int return SDL_atomic_t return void void void return void return int return SDL_AudioSpec SDL_AudioSpec return int int return return int SDL_RWops int SDL_AudioSpec Uint8 Uint32 * e
static void release(HIDDeviceManager manager)
static void loadLibrary(String libraryName)
static HIDDeviceManager acquire(Context context)
boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice)
int sendOutputReport(int deviceID, byte[] report)
static screen_context_t context
native void HIDDeviceRegisterCallback()
boolean openDevice(int deviceID)
final BroadcastReceiver mBluetoothBroadcast
boolean isHIDDeviceUSB(UsbDevice usbDevice)
boolean getFeatureReport(int deviceID, byte[] report)
List< BluetoothDevice > mLastBluetoothDevices
void setFrozen(boolean frozen)
void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice)
static SDL_AudioDeviceID device
SharedPreferences mSharedPreferences
void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted)
HashMap< UsbDevice, HIDDeviceUSB > mUSBDevices
boolean isSteamController(BluetoothDevice bluetoothDevice)
int getDeviceIDForIdentifier(String identifier)
void initializeBluetooth()
static HIDDeviceManager sManager
final BroadcastReceiver mUsbBroadcast
boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number)
static int sManagerRefCount
void handleUsbDeviceDetached(UsbDevice usbDevice)
HIDDeviceManager(final Context context)
boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface)
HashMap< BluetoothDevice, HIDDeviceBLESteamController > mBluetoothDevices
void closeDevice(int deviceID)
void chromebookConnectionHandler()
BluetoothManager mBluetoothManager
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
static final String ACTION_USB_PERMISSION