When the application is already opened, and a USBDevice is attached, the application is sometimes able to communicate with the device, and sometimes it doesnt. The error's I receive differ every time. The constant is, it works as expected on a Samsung Galaxy phone, Nexus 7, Nexus 10. If the application launches from the device being connected, everything works as expected, most of the time.
Manifest is set up like so
<uses-feature android:name="android.hardware.usb.host" />
<uses-feature android:name="android.hardware.usb.UsbInterface" />
<activity android:label="@string/app_name"
android:screenOrientation="fullSensor" android:launchMode="singleTask"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"
android:name="com.realart.Beatmaker.UnityPlayerNativeActivity">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter >
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<!-- USB Connect List -->
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
<meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
</activity>
In the onCreate, I check to see if the application was launched from an intent, by:
// Get the device that woke up the activity
UsbDevice connectedDevice = (UsbDevice)getIntent()
.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (connectedDevice != null) {
UsbManager manager = (UsbManager)getSystemService(Context.USB_SERVICE);
UsbDevice device = connectedDevice;
writeDeviceInfo(device);
initDataTransferThread(manager, device);
}
If the application is already opened, and because launchMode
is singleTask
, I've Overridden the onNewIntent
method like so
@Override
public void onNewIntent(Intent intent) {
final String DEVICE_CONNECTED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
String action = intent.getAction();
if (action.equals(DEVICE_CONNECTED)) {
Log.d(TAG, "Device connected");
final UsbManager manager = (UsbManager)getSystemService(Context.USB_SERVICE);
try {
final UsbDevice connectedDevice = (UsbDevice)intent
.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if (connectedDevice != null) {
writeDeviceInfo(connectedDevice);
initDataTransferThread(manager, connectedDevice);
} else {
Log.d(TAG, "Device null");
}
} else {
Log.d(TAG, "Permission denied");
Log.d(TAG, "Asking for permission and trying again...");
manager.requestPermission(connectedDevice, permissionIntent);
}
} catch (Exception e) {
Log.d(TAG, "Exception throw: " + e.toString());
e.printStackTrace();
}
}
}
And it almost always hits the permission denied else block in that method, so it then goes to the permission check broadcast receiver I have set up like so:
// Attach Events
attachReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Permissions receiver hit");
String action = intent.getAction();
Log.d(TAG, "Action requested: " + action);
UsbManager manager = (UsbManager)getSystemService(Context.USB_SERVICE);
if (intent.getAction().equals("com.realart.Beatmaker.USB_PERMISSION")) {
// Thread still running?
if (arduino != null) {
if (arduino.pipeline != null) {
Message msg = Message.obtain();
msg.what = Change.USB_DISCONNECTED;
arduino.pipeline.sendMessage(msg);
}
arduino = null;
}
synchronized (this) {
Log.d(TAG, "Checking permissions from user...");
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
boolean permissionGranted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
Log.d(TAG, "Permissions granted: " + permissionGranted);
if (permissionGranted) {
if (device != null) {
writeDeviceInfo(device);
initDataTransferThread(manager, device);
} else {
Log.d(TAG, "Device null");
}
} else {
Log.d(TAG, "Permission denied from user");
}
}
}
}
};
permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent("com.realart.Beatmaker.USB_PERMISSION"), 0);
IntentFilter attachFilter = new IntentFilter("com.realart.Beatmaker.USB_PERMISSION");
registerReceiver(attachReceiver, attachFilter);
That process doesn't seem to be causing any errors, and the device info writes out every time. Sometimes, it lists two interfaces, sometimes 0. Which is weird, and I dunno why it would do that.
This is how I am initializing the device:
private boolean initSerialBus() {
try {
// Open device
connection = usbManager.openDevice(arduino);
if (connection == null) {
Log.d(TAG, "Opening device failed");
return false;
}
// Get serial interface
usbInterface = arduino.getInterface(1);
boolean interfaceClaimed = connection.claimInterface(usbInterface, FORCE_CLAIM);
Log.d(TAG, "USB Interface claimed: " + interfaceClaimed);
if (!interfaceClaimed) {
Log.d(TAG, "Trying again with kernel driver");
interfaceClaimed = connection.claimInterface(usbInterface, false);
if (!interfaceClaimed) {
Log.d(TAG, "Unable to claim interface.");
return false;
}
}
// Arduino USB serial converter setup
Log.d(TAG, "Data Transfter setup");
// Line state
int lineState = connection.controlTransfer(0x21, 0x22, 0, 0, null, 0, 0);
if (lineState < 0) {
Log.d(TAG, "Line state control transfer failed.");
Log.d(TAG, "Return value: " + lineState);
releaseUsbResources();
arduinoState.onArduinoError(new Exception("Control frame transfer error"));
return false;
}
Log.d(TAG, "Line state control frame: OK return: " + lineState);
// Line encoding (9600 Baud)
final byte[] lineEncoding = {
(byte) 0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08
};
int lineEncode = connection.controlTransfer(0x21, 0x20, 0, 0, lineEncoding, 7, 0);
if (lineEncode < 0) {
Log.d(TAG, "Line encoding control transfer failed.");
Log.d(TAG, "Return value: " + lineEncode);
releaseUsbResources();
arduinoState.onArduinoError(new Exception("Control frame transfer error"));
return false;
}
Log.d(TAG, "Line encoding control frame: OK return: " + lineEncode);
// I/O Endpoints
for (int i = 0; i < usbInterface.getEndpointCount(); i++) {
UsbEndpoint endpoint = usbInterface.getEndpoint(i);
if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
arduinoIn = usbInterface.getEndpoint(i);
} else {
arduinoOut = usbInterface.getEndpoint(i);
}
}
}
// Ensure we found the endpoints
if (arduinoIn == null || arduinoOut == null) {
releaseUsbResources();
arduinoState.onArduinoError(new Exception("No usb endpoints found"));
return false;
}
Log.d(TAG, "I/O Enpoints found");
arduinoConnected = true;
} catch (Exception e) {
e.printStackTrace();
}
return arduinoConnected;
}
This is the usual logcat output:
V/UsbHostManager( 2733): USB HOST UEVENT: {SUBSYSTEM=host_notify, STATE=ADD, DEVNAME=usb_otg, DEVPATH=/devices/virtual/host_notify/usb_otg, SEQNUM=18860, ACTION=change}
D/UsbHostManager( 2733): turnOnLcd ::
D/UsbHostNotification( 2733): setUsbObserverNotification : cancel id = 998563545, device = UsbDevices
D/UsbHostNotification( 2733): setUsbObserverNotification : notify id = 998563545, device = UsbDevices, title = USB connector connected.
D/UsbHostNotification( 2733): send the timeout : current 1413981920804, vailed = -2999, displayed = 1413981920803
D/UsbHostNotification( 2733): setUsbObserverNotification : cancel id = 998563545, device = UsbDevices
D/UsbHostManager( 2733): usbDeviceAdded : device :: /dev/bus/usb/002/077 [2341h:003dh] [02h,00h,00h] (CDC Control)
D/UsbHostManager( 2733): usbDeviceAdded : interface :: /dev/bus/usb/002/077 [2341h:003dh] [02h,02h,01h] (CDC Control)
D/UsbHostManager( 2733): usbDeviceAdded : interface :: /dev/bus/usb/002/077 [2341h:003dh] [0ah,00h,00h] (CDC Data)
D/UsbSettingsManager( 2733): deviceAttached: /dev/bus/usb/002/077 def package com.realart.Beatmaker
D/UsbSettingsManager( 2733): deviceAttached, send intent Intent { act=android.hardware.usb.action.USB_DEVICE_ATTACHED flg=0x10000000 (has extras) }
D/UsbSettingsManager( 2733): deviceAttached, call to resolveActivity started, Intent { act=android.hardware.usb.action.USB_DEVICE_ATTACHED flg=0x10000000 (has extras) }
D/UsbSettingsManager( 2733): resolveActivity : matches count = 1, defaultPackage = com.realart.Beatmaker
D/UsbSettingsManager( 2733): resolveActivity : defaultRI = ResolveInfo{425e9ad0 com.realart.Beatmaker/.UnityPlayerNativeActivity m=0x108000}
D/UsbSettingsManager( 2733): grantDevicePermission: UsbDevice[mName=/dev/bus/usb/002/077,mVendorId=9025,mProductId=61,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.hardware.usb.UsbInterface;@42d33850] for 10212
D/UsbSettingsManager( 2733): grantDevicePermission: mDevicePermissionMap put UsbDevice[mName=/dev/bus/usb/002/077,mVendorId=9025,mProductId=61,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.hardware.usb.UsbInterface;@42d33850] for {}
D/UsbSettingsManager( 2733): resolveActivity : permissionsGranted to ResolveInfo{425e9ad0 com.realart.Beatmaker/.UnityPlayerNativeActivity m=0x108000}
D/UsbSettingsManager( 2733): deviceAttached, call to resolveActivity ended, Intent { act=android.hardware.usb.action.USB_DEVICE_ATTACHED flg=0x10000000 cmp=com.realart.Beatmaker/.UnityPlayerNativeActivity (has extras) }
D/Unity (30871): Device connected
D/Unity (30871): Permission denied
D/Unity (30871): Asking for permission and trying again...
D/UsbSettingsManager( 2733): requestPermission:/dev/bus/usb/002/077 ,pi PendingIntent{42169a08: PendingIntentRecord{42c42748 com.realart.Beatmaker broadcastIntent}}
D/UsbSettingsManager( 2733): Request Permission for Device vendorId: 9025, productId: 61, package: com.realart.Beatmaker
D/UsbSettingsManager( 2733): hasPermission++
D/UsbSettingsManager( 2733): hasPermission: UsbDevice[mName=/dev/bus/usb/002/077,mVendorId=9025,mProductId=61,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.os.Parcelable;@4360cc38]for {10212=true}
D/UsbSettingsManager( 2733): requestPermission:/dev/bus/usb/002/077 has permissions
D/Unity (30871): Permissions receiver hit
D/Unity (30871): Action requested: com.realart.Beatmaker.USB_PERMISSION
D/Unity (30871): Checking permissions from user...
D/Unity (30871): Permissions granted: true
D/Unity (30871): Device name: /dev/bus/usb/002/077
D/Unity (30871): Vender id: 9025
D/Unity (30871): Product id: 61
D/Unity (30871): Class: class android.hardware.usb.UsbDevice
D/Unity (30871): Sub-class: 0
D/Unity (30871): Protocol: 0
D/Unity (30871): Num Interfaces: 2
D/Unity (30871): Device hash: 263427216
D/Arduino (30871): USB Interface claimed: false
D/Arduino (30871): Trying again with kernel driver
D/Arduino (30871): Unable to claim interface.
D/UsbSettingsManager( 2733): checkPermission: /dev/bus/usb/002/077
D/UsbSettingsManager( 2733): hasPermission++
D/UsbSettingsManager( 2733): hasPermission: UsbDevice[mName=/dev/bus/usb/002/077,mVendorId=9025,mProductId=61,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.hardware.usb.UsbInterface;@42d33850]for {10212=true}
There seems to be a noticeable timeout after the following lines are printed in logcat:
D/UsbHostNotification( 2733): send the timeout : current 1413981920804, vailed = -2999, displayed = 1413981920803
D/UsbHostNotification( 2733): setUsbObserverNotification : cancel id = 998563545, device = UsbDevices
If the rest of the application continues fast enough, the problem doesn't happen, but if it hangs on that step for a few seconds, it fails every time. But, not always in the same spot. Sometimes when trying to claim the interface, sometimes when sending the control transfers, and sometimes no failure message, but no data is being received.
When the application is launched, the logcat output looks like this:
--------- beginning of /dev/log/system
D/UsbHostNotification( 2733): setUsbObserverNotification : cancel id = 998563545, device = UsbDevices
--------- beginning of /dev/log/main
V/UsbHostManager( 2733): USB HOST UEVENT: {SUBSYSTEM=host_notify, STATE=ADD, DEVNAME=usb_otg, DEVPATH=/devices/virtual/host_notify/usb_otg, SEQNUM=19659, ACTION=change}
D/UsbHostManager( 2733): turnOnLcd ::
D/UsbHostNotification( 2733): setUsbObserverNotification : cancel id = 998563545, device = UsbDevices
D/UsbHostNotification( 2733): setUsbObserverNotification : notify id = 998563545, device = UsbDevices, title = USB connector connected.
D/UsbHostNotification( 2733): send the timeout : current 1413982261198, vailed = -2991, displayed = 1413982261189
D/UsbHostManager( 2733): usbDeviceAdded : device :: /dev/bus/usb/002/081 [2341h:003dh] [02h,00h,00h] (CDC Control)
D/UsbHostManager( 2733): usbDeviceAdded : interface :: /dev/bus/usb/002/081 [2341h:003dh] [02h,02h,01h] (CDC Control)
D/UsbHostManager( 2733): usbDeviceAdded : interface :: /dev/bus/usb/002/081 [2341h:003dh] [0ah,00h,00h] (CDC Data)
D/UsbSettingsManager( 2733): deviceAttached: /dev/bus/usb/002/081 def package com.realart.Beatmaker
D/UsbSettingsManager( 2733): deviceAttached, send intent Intent { act=android.hardware.usb.action.USB_DEVICE_ATTACHED flg=0x10000000 (has extras) }
D/UsbSettingsManager( 2733): deviceAttached, call to resolveActivity started, Intent { act=android.hardware.usb.action.USB_DEVICE_ATTACHED flg=0x10000000 (has extras) }
D/UsbSettingsManager( 2733): resolveActivity : matches count = 1, defaultPackage = com.realart.Beatmaker
D/UsbSettingsManager( 2733): resolveActivity : defaultRI = ResolveInfo{42458358 com.realart.Beatmaker/.UnityPlayerNativeActivity m=0x108000}
D/UsbSettingsManager( 2733): grantDevicePermission: UsbDevice[mName=/dev/bus/usb/002/081,mVendorId=9025,mProductId=61,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.hardware.usb.UsbInterface;@4259eeb8] for 10212
D/UsbSettingsManager( 2733): grantDevicePermission: mDevicePermissionMap put UsbDevice[mName=/dev/bus/usb/002/081,mVendorId=9025,mProductId=61,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.hardware.usb.UsbInterface;@4259eeb8] for {}
D/UsbSettingsManager( 2733): resolveActivity : permissionsGranted to ResolveInfo{42458358 com.realart.Beatmaker/.UnityPlayerNativeActivity m=0x108000}
D/UsbSettingsManager( 2733): deviceAttached, call to resolveActivity ended, Intent { act=android.hardware.usb.action.USB_DEVICE_ATTACHED flg=0x10000000 cmp=com.realart.Beatmaker/.UnityPlayerNativeActivity (has extras) }
D/UsbHostManager( 2733): onUEvent(device) :: action = add, devtype = usb_interface, device = null, product = 2341/3d/1, type = 2/0/0, interface = 2/2/1, devpath = /devices/platform/exynos-dwc3.0/exynos-xhci.0/usb2/2-1/2-1:1.0
D/UsbHostManager( 2733): onUEvent(device) :: action = add, devtype = usb_interface, device = null, product = 2341/3d/1, type = 2/0/0, interface = 10/0/0, devpath = /devices/platform/exynos-dwc3.0/exynos-xhci.0/usb2/2-1/2-1:1.1
D/Unity (31983): Device name: /dev/bus/usb/002/081
D/Unity (31983): Vender id: 9025
D/Unity (31983): Product id: 61
D/Unity (31983): Class: class android.hardware.usb.UsbDevice
D/Unity (31983): Sub-class: 0
D/Unity (31983): Protocol: 0
D/Unity (31983): Num Interfaces: 2
D/Unity (31983): Device hash: 263427241
D/Arduino (31983): USB Interface claimed: true
D/Arduino (31983): Data Transfter setup
D/Arduino (31983): Line state control frame: OK return: 0
D/Arduino (31983): Line encoding control frame: OK return: 7
D/Arduino (31983): I/O Enpoints found
D/Arduino (31983): Arduino bus initialized
D/UsbSettingsManager( 2733): checkPermission: /dev/bus/usb/002/081
D/UsbSettingsManager( 2733): hasPermission++
D/UsbSettingsManager( 2733): hasPermission: UsbDevice[mName=/dev/bus/usb/002/081,mVendorId=9025,mProductId=61,mClass=2,mSubclass=0,mProtocol=0,mInterfaces=[Landroid.hardware.usb.UsbInterface;@4259eeb8]for {10212=true}
D/Arduino (31983): Receiver thread started
D/Unity (31983): onArduinoReady
D/Arduino (31983): resetArduino
V/Unity (31983): sending message to unity = reset
V/Unity (31983): sending message to unity = onArduinoReady
When the application is started, device connected, and the onNewIntent
method is hit, the permissions denied block is hit every time, but the prompt asking for permissions is never shown?
Does anyone have any idea why this behavior is so strange with this tablet?
Model: SM-T800
Android Version: 4.4.2
Kernel Version: 3.4.39-2010469
Build Number: KOT49H.T800XXU1ANFB
In case anyone hits this with the same problem, the main section of the app was created with Unity and it was a race condition between the spawned thread that reacted to the intent, and Unity on grabbing the UsbInterface. On boot, because Unity wasn't initialized yet, the spawned thread was able to capture the interface 99% of the time, but after Unity was already spawned up, Untiy through the JNI was able to claim the interface first. I'm not entirely sure why it was claiming it as a sensor on this tablet, and not other ones, but probably has something to do with the Exynos 5 Octa 5420
chipset and how Unity decides something is a sensor.