Michael
Michael

Reputation: 910

Detecting when a USB device is detached on Android

I've got an Android app that needs to detect when a USB peripheral is attached or detached. It works fine when the peripheral is first attached, but I don't receive any notification (i.e., I don't receive an Intent whose action is ACTION_USB_DEVICE_DETACHED) when it is subsequently detached.

Here's the relevant part of my AndroidManifest.xml:

<activity android:name=".LauncherActivity">
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
        <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
    </intent-filter>
    <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" />
    <meta-data android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" android:resource="@xml/device_filter" />
</activity>

It may also be worth noting that LauncherActivity only exists to start a Service when the device is attached, and to stop the service when it is detached. In either case, LauncherActivity always finishes itself immediately. All of this occurs in LauncherActivity.onCreate.

Any ideas?

Upvotes: 16

Views: 30155

Answers (5)

techwinder
techwinder

Reputation: 11

The fix that worked for me was to unregister in onPause() and register again in onResume():

@Override
public void onPause()
{
    super.onPause();
    stopIOManager();
    if(m_UsbReceiver!=null) unregisterReceiver(m_UsbReceiver);
}

@Override
public void onResume()
{
    super.onResume();
    startIOManager();
    IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
    registerReceiver(m_UsbReceiver, filter);
}

However, although the app seems to always receive the DETACH event, once in a while it doesn't get the ATTACHED event. I then need to plug and and unplug the USB connector, and it usually works after one or two attempts. I've put the blame of this strange behaviour on the OS, without certainty.

Upvotes: 1

techwinder
techwinder

Reputation: 11

For what it's worth, I had this same issue because the activity is paused then resumed when the device is disconnected.
Since the receiver is unregistered in the OnPause() method just prior to receiving the ACTION_USB_DEVICE_DETACHED, your app never gets notified.

Upvotes: 0

prashanth yet
prashanth yet

Reputation: 259

Try with USB_STATE as below.

It will fire both attached and detatched to same receiver and in receiver you can identify whether is was attached or detatched event.

IntentFilter filter = new IntentFilter();
filter.addAction("android.hardware.usb.action.USB_STATE");

Receiver:

public class USBReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        if (intent.getExtras().getBoolean("connected")) { 
             //do your stuff
        }
    }  
}

Upvotes: 11

Big
Big

Reputation: 247

USB_DEVICE_DETACHED is a broadcast intent, thus you may want to declare the BroadcastReceiver in manifest with the appropriate intent-filter for detached action, also with meta-data attached. Same goes for USB_ACCESSORY_DETACHED, for who is interested.

Summary:
USB_XXX_ATTACHED is an activity intent
USB_XXX_DETACHED is a broadcast intent

(where XXX = DEVICE | ACCESSORY)

See: http://developer.android.com/guide/components/intents-filters.html

"There is no overlap within these messaging systems: Broadcast intents are delivered only to broadcast receivers, never to activities or services"

Upvotes: 19

Michael
Michael

Reputation: 910

So, I never got the ACTION_USB_DEVICE_DETACHED Intent to go to LauncherActivity; I don't know what the deal is there, probably something I don't properly understand about intent filters or the Activity lifecycle callbacks.

The solution I ended up using comes from the post linked by Pratik. I basically took everything about USB_DEVICE_DETACHED out of AndroidManifest.xml. Then, in the onCreate method of the Service, I registered a BroadcastReceiver like this:

@Override
public void onCreate() {
    detachReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED))
                stopSelf();
        }
    };

    IntentFilter filter = new IntentFilter();
    filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
    registerReceiver(detachReceiver, filter);
}

A little clunky, and I'm still curious why just adding USB_DEVICE_DETACHED to the <intent-filter> of LauncherActivity wasn't working, but it does what I need.

Upvotes: 9

Related Questions