Android Usb Host, singleInstance, onCreate keeps getting called with USB attach

1k views Asked by At

I have an application that uses a proprietary USB device (android in host mode).

I have a BroadcastReceiver which I register in the constructor

// this block is in the constructor
usbConnectionReceiver = new UsbConnectionReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
context.registerReceiver(usbConnectionReceiver, filter);



public class UsbConnectionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
            UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) onDeviceAttached(device);
        } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
            UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) onDeviceDetached(device);
        }
    }
}

public void onDeviceAttached(UsbDevice device) {
    usbDevice = device;
    intf = usbDevice.getInterface(0);
    endpoint = intf.getEndpoint(0);
    if (usbManager.hasPermission(usbDevice)) {
        usbDeviceConnection = usbManager.openDevice(usbDevice);
        boolean status = usbDeviceConnection.claimInterface(intf, true);
        if (status) {
            startPollingThread();
        }
    } else {
        // I used to have code here that would programmatically prompt for permissions.
    }
}

Everything works great, except that every time the device is detached and reattached, the end-user is prompted for permissions. The "Use by default for this USB device" checkbox is completely ignored.

After some research, it appears that if I ask for permissions via code, that is indeed the case.

So I've switched my activity to use an intent filter. I no longer get the permissions box after the first time, so the checkbox is working. Instead, every time the device is reattached, it calls onCreate() of my app, crashing it.

I have the launchMode="singleInstance" (in fact, I've tried all varieties), and while the app is up and running and top on the screen, the onCreate() is called instead of the onNewIntent() (which the documentation seems to suggest should happen).

I also read that I need to set android:launchMode="singleInstance" I also read I need to set android:taskAffinity=""

All to no avail. I know there are tons of similar posts, but none of them seem to work. Any advice is appreciated.

Here is my AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.me.myapp" >
    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
        android:launchMode="singleInstance" >
        <activity
            android:name=".MainActivity"
            android:taskAffinity=""
            android:label="@string/app_name"
            android:configChanges="keyboard" >
            <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>
            <meta-data
                android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                android:resource="@xml/device_filter" />
        </activity>
    </application>
</manifest>
1

There are 1 answers

0
jo phul On

I believe this is the best solution ... if anyone has a comment, I'd love to hear it because Usb handling in android has always been a headache!

I used this post as a reference USB device access pop-up supression?

I created the following Activity

public class UsbActivity extends Activity {
    private static final String TAG = "UsbActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onResume()
    {
        super.onResume();
        Intent intent = getIntent();
        if (intent != null)
        {
            if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED))
            {
                Logger.lc(TAG,"ACTION_USB_DEVICE_ATTACHED");
                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                Logger.lc(TAG,"UsbDevice");
                UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
                Logger.lc(TAG,"UsbManager");
                if (usbDevice != null && usbManager != null && !usbManager.hasPermission(usbDevice)) {
                    Logger.lc(TAG,"openDevice");
                    usbManager.openDevice(usbDevice);
                }
            }
        }
        finish();
    }
}

And added it to the manifest

<activity
        android:name="com.myapp.UsbActivity"
        android:label="MyApp"
        android:theme="@style/Theme.Transparent"
        android:noHistory="true"
        android:excludeFromRecents="true"
        android:taskAffinity="com.myapp.taskAffinityUsbReceiver"
        android:process=":UsbEventReceiverActivityProcess"
        android:exported="false" >
        <intent-filter>
            <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
        </intent-filter>
        <meta-data
            android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
            android:resource="@xml/device_filter" />
</activity>

with the following style.xml

   <style name="Theme.Transparent" parent="android:Theme">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:backgroundDimEnabled">false</item>
        <item name="android:windowAnimationStyle">@null</item>
   </style>