Android UsbHost问题与三星Galaxy Tab S 10.5和Unity。

时间:2021-10-28 18:57:56

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.

当应用程序已经打开,并且附加了USBDevice时,应用程序有时能够与设备进行通信,有时则不能。每次都有不同的错误。这个常数是,它在三星Galaxy手机,Nexus 7, Nexus 10上运行。如果应用程序从正在连接的设备上启动,那么大多数情况下,一切正常。

Manifest is set up like so

Manifest是这样设置的。

<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:

在onCreate中,我检查应用程序是否从一个意图启动:

// 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

如果应用程序已经打开,并且由于launchMode是singleTask,我已经重写了像这样的onNewIntent方法。

@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.

这个过程似乎不会造成任何错误,而且设备信息每次都会写出来。有时,它列出两个接口,有时是0。这很奇怪,我不知道为什么会这样。

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:

这是通常的logcat输出:

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:

在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:

当应用程序启动时,logcat输出如下:

--------- 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?

当启动应用程序时,设备连接,并且onNewIntent方法被命中,每次都被拒绝的权限块被击中,但是请求权限的提示从未显示出来?

Does anyone have any idea why this behavior is so strange with this tablet?

有没有人知道为什么这款平板电脑的行为如此怪异?

Model: SM-T800

模型:SM-T800

Android Version: 4.4.2

Android版本:10/24/11

Kernel Version: 3.4.39-2010469

内核版本:3.4.39-2010469

Build Number: KOT49H.T800XXU1ANFB

构建数字:KOT49H.T800XXU1ANFB

1 个解决方案

#1


0  

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.

如果有人碰到同样的问题,应用程序的主要部分是统一创建的,这是一个在派生的线程之间的竞态条件,它响应了意图,并统一了获取UsbInterface。在启动时,因为Unity还没有初始化,所以生成的线程能够在99%的时间内捕获接口,但是在Unity已经生成之后,直到JNI能够首先声明接口。我不太清楚为什么它会把它作为一个传感器在这个平板电脑上,而不是其他的,但是可能和Exynos 5 Octa 5420芯片组有关,以及Unity如何决定某物是一个传感器。

#1


0  

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.

如果有人碰到同样的问题,应用程序的主要部分是统一创建的,这是一个在派生的线程之间的竞态条件,它响应了意图,并统一了获取UsbInterface。在启动时,因为Unity还没有初始化,所以生成的线程能够在99%的时间内捕获接口,但是在Unity已经生成之后,直到JNI能够首先声明接口。我不太清楚为什么它会把它作为一个传感器在这个平板电脑上,而不是其他的,但是可能和Exynos 5 Octa 5420芯片组有关,以及Unity如何决定某物是一个传感器。