USB_DEVICE_ATTACHED意图未触发

28

有没有人成功使用"android.hardware.usb.action.USB_DEVICE_ATTACHED"来检测USB设备连接的情况?

我正在尝试使用新的USB主机模式功能来检测USB设备的连接。为了我的目的,我希望在任何时候都能收到关于设备连接的通知。但是我无法看到它发生。我正在使用已知可以工作的广播接收器(当我让它监听其他事件,例如按下Home按钮)。无论我尝试什么,我似乎都无法触发意图......所以为了简化事情,我决定放弃自己的项目,尝试使用谷歌的示例代码,看看能否至少让那个代码运行起来。虽然我没有导弹发射器,但我想至少能让USB_Device_Attached这个意图启动。但依旧失败了。我尝试调整设备过滤器xml文件。首先,我尝试添加我的设备(一个键盘):

<usb-device vendor-id="1050" product-id="0010" />

我从lsusb命令中获取了设备的供应商信息和产品信息。当设备连接时,logcat显示已经找到该设备。

D/EventHub(  144): No input device configuration file found for device 'Yubico Yubico Yubikey II'.
I/EventHub(  144): New device: id=43, fd=219, path='/dev/input/event8', name='Yubico Yubico Yubikey II', classes=0x80000003, configuration='', keyLayout='/system/usr/keylayout/Generic.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false
I/InputReader(  144): Device added: id=43, name='Yubico Yubico Yubikey II', sources=0x00000101
I/ActivityManager(  144): Config changed: { scale=1.0 imsi=0/0 loc=en_US touch=3 keys=2/1/1 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=47}
D/MissileLauncherActivity(16191): intent: android.intent.action.MAIN
I/EventHub(  144): Removed device: path=/dev/input/event8 name=Yubico Yubico Yubikey II id=43 fd=219 classes=0x80000003
I/InputReader(  144): Device removed: id=43, name='Yubico Yubico Yubikey II', sources=0x00000101
I/ActivityManager(  144): Config changed: { scale=1.0 imsi=0/0 loc=en_US touch=3 keys=1/1/2 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=48}
D/dalvikvm(  144): GC_EXPLICIT freed 78K, 26% free 14717K/19719K, paused 3ms+3ms
D/MissileLauncherActivity(16191): intent: android.intent.action.MAIN

xoom可以找到键盘,而且可以从设备上使用它(我可以在浏览器中使用它输入字母)。并且意图有点触发了(但它只会触发android.intent.action.MAIN),我从未收到DEVICE_ATTACHED Intent。日志条目来自示例代码:

Log.d(TAG, "intent: " + intent.getAction().toString());
在恢复函数中,经过更深入的挖掘并删除任何对usb的引用后,我发现当键盘连接/断开时(因此出现了intent: android.intent.action.MAIN日志条目),我制作的每个应用程序都会调用恢复函数。 目前我能想到的唯一解释是这是Android源代码中的一个bug。 顺便说一下,我正在使用OS 3.1版本的wifi xoom。

我本来想建议查看平台源代码以弄清楚它真正应该做什么......然后我想起来了 :-( 不过有一个想法 - 你周围有没有任何非HID设备的USB小玩意儿可以尝试? - Chris Stratton
11个回答

24
我也遇到了同样的问题。我最终发现,在设备筛选器XML中,我们应该添加以下行。
<usb-device vendor-id-"xxxxx" product-id="yyyyy">

xxxxx和yyyyy应该是十进制数,而不是十六进制代码。这样就可以按照广告上的描述正常工作了!虽然有些晚,但我希望它能有所帮助。


5
<usb-device vendor-id="3034" product-id="33159"/> - Bryce Thomas
vendor-id和product-id应该是整数。不要使用十六进制。 - Xar-e-ahmer Khan

16

所以我找到了解决方案,学到了很多东西,希望可以帮助其他人。

首先,HID设备不会触发任何意图。它们也不会显示在mUsbManager.getDeviceList()列表中。然而,其他东西会出现在列表中。我试用了一个USB存储器,这个设备出现在设备列表中。我还发现返回的设备没有类、子类或协议。调试揭示,父接口确实具有正确的类/子类/和协议。 如果您必须有一个设备过滤器,我最终使用了 class=0008 (USB STORAGE) 来达到我的目的。我猜其他类也可以工作。

现在到了解决意图的时候。原来意图必须附加到启动器活动上。将其附加到服务或接收器上是无效的。现在当我收到意图时,我看到通知弹出窗口,提醒我设置我的应用程序为此设备的默认值。完美,现在每次连接该设备时都运行我的应用程序。请注意,对于每个唯一的设备,您都需要进行提示。但只需要一次。它似乎像默认程序一样注册。

好吧,我认为这大约总结了我发现的内容。很遗憾,您无法在键盘/鼠标连接时收到通知。哦,还有一件事。Tiamat内核没有任何问题,现在正在运行它,没有问题。


是的,某些HID设备会触发意图(请参见使用HID设备的MissileLauncher示例应用程序)。 其中 InterfaceSubClass = 0x00InterfaceProtocol = 0x00 的设备会导致意图发生。 有关更多信息,请参见我的答案。 - Ryan R

11

我最近发现了一个解决类似问题的方法。

正如已经有人指出的那样,HID设备不会触发意图,我认为这可能是你遇到的问题。

但是,一个相关的问题是,如果你的程序设置成在连接USB设备后运行,即使你的应用程序正在运行,你也无法捕获USB_DEVICE_ATTACHED操作。相反,系统会看到该意图,并说“哦,这意味着这个应用程序想要运行(如在你的清单中声明的那样),然后它会发送android.intent.action.MAIN而不是USB_DEVICE_ATTACHED操作,并调用onResume()。即使你的应用程序正在运行,当USB设备连接时,onResume()也会再次被调用。因此,据我所知,如果你的清单声明了你的应用程序将在USB设备连接时运行,那么你无法捕获USB_DEVICE_ATTACHED意图。你只需要在onResume()中放置一些代码来检查是否连接了USB即可。

我在这里详细说明我的解决方案:Android 3.1 USB-Host - BroadcastReceiver does not receive USB_DEVICE_ATTACHED


3

枚举设备

如果您的应用程序在运行时对当前连接的所有USB设备进行检查,可以枚举总线上的设备。使用getDeviceList()方法获取已连接的所有USB设备的哈希映射。如果您想从地图中获取设备,则哈希映射的键为USB设备的名称。

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();

如果需要的话,您也可以从哈希映射中获取迭代器,并逐个处理每个设备:
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
    UsbDevice device = deviceIterator.next()
    //your code
}

3

我曾经遇到过同样的问题。我的最终解决方案是使用老式轮询技术。以下是一个相当简单的类,可以满足我的需求并解决该问题。

package com.YourCompancy.YourProduct;

import android.app.*;
import android.content.*;

import android.hardware.usb.*;
import java.util.*;
import android.util.*;
import android.os.*;

public class UsbDeviceWatcher extends BroadcastReceiver
{   
    public void onReceive(Context context, Intent intent)
    {
        if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED))
        {
            UsbDevice d = (UsbDevice)
                intent.getExtras().get(UsbManager.EXTRA_DEVICE);

            DeviceConnect(d, false);
        }
    }

    public void DeviceConnect(UsbDevice device, boolean Attached)
    {
            if (Attached)
            {
                            // Some suggestions ...
                            //    play sound effect
                            //    notify consumer software
                            //    determine if interested in device
                            //    etc
                            Log.i("usb", "device attached");

            } else
            {
                Log.i("usb", "device detached");
            }

    }

    public UsbManager manager;
    public Handler handler;

    public UsbDeviceWatcher(Context context, Handler handle)
    {

        this.handler = handle;

        manager = (UsbManager) 
            context.getSystemService(Context.USB_SERVICE);

        IntentFilter dev = new IntentFilter();

        dev.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);

        context.registerReceiver(this, dev);    

        final UsbDeviceWatcher _this = this;

        Thread thread = new Thread(new Runnable()
        {
            public void run()
            {
                LinkedList<UsbDevice> seen = new LinkedList<UsbDevice>();
                LinkedList<UsbDevice> attached = new LinkedList<UsbDevice>();

                            //there is a need for multithread support here
                                //   so the thread can watch for an exit condition
                while (true)
                {

                    HashMap<String, UsbDevice>
                        D = manager.getDeviceList();

                    for (UsbDevice d : D.values())
                    {
                        if (!seen.contains(d))
                        {
                            if (!attached.contains(d))
                            {
                                final UsbDevice dev = d;

                                handler.post(new Runnable(){
                                    public void run()
                                    {
                                        DeviceConnect(dev, true);
                                    }
                                });
                            }

                            seen.add(d);
                        }
                    }

                    for (UsbDevice d : seen)
                    {
                        if (!D.values().contains(d)) seen.remove(d);
                    }

                    try
                    {
                        Thread.sleep(500);  
                    } catch (InterruptedException exception)
                    {
                        return; 
                    }
                }

            }
        });

        thread.start();
    }
}

2
另一种解决方法是使用:
new FileObserver("/dev/input") {
  @Override public void onEvent(int event, String path) {
     //gets called on input device insert / remove
  }
};

这将适用于一些USB设备(键盘、鼠标)。


2

我将我的应用设置为launchMode="singleTop",在这种模式下,似乎getIntent().getAction()始终等于首次启动应用程序的操作。

因此,如果你手动启动应用程序,然后插入设备(即使在切换到其他应用程序之后),你将接收到android.intent.action.MAIN

如果你杀死应用程序,然后再插入设备,即使从切换到你的应用程序,或者甚至是旋转设备,你仍将始终收到android.hardware.usb.action.USB_DEVICE_ATTACHED

实际上,在拔出USB设备时,我奇怪地收到了一些意图,我认为这并没有被记录在文档中 - 但当然,当我的设备被卸载时,我会收到USB_DEVICE_ATTACHED

没有使用singleTop时,它确实有点像预期的工作方式,但是如果你的应用程序已经打开,并且插入设备,则会获得另一个愚蠢的额外活动。

再次说明,Android的API存在漏洞,过于复杂和难以使用。


1
这是我用来检测USB/媒体连接的方法。
清单文件
    <receiver
            android:name=".UsbReceiver"
            android:enabled="true" >
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_MOUNTED"/>
            <action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
            <data android:scheme="file"/>
        </intent-filter>
    </receiver>

我在活动中和我的接收器都没有做任何事情。
看起来这行正在处理事情。
<data android:scheme="file"/>

1

做更多的工作,失败也会更多,但是有一些进展。

我从sdk文档中了解到更多信息。看起来你必须拥有设备过滤器才能使用意图。所以我决定尝试使用类过滤器而不是供应商/产品ID。我认为这样会更通用,希望能捕捉到HID设备。我使用03h作为类ID,尝试了各种格式,尝试了子类,甚至使用lsusb发现了我的设备的类、子类和协议。这些似乎没有什么帮助。所以我进一步阅读了sdk文档,并决定尝试枚举所有设备,看看操作系统如何看待类/子类/协议整数。我复制了代码,将其粘贴到单击监听器中,并添加了log.v语句。在logcat中没有显示任何内容。

看起来,美国系统似乎没有检测到任何设备(尽管设备实际上是工作的)。这非常明显地表明USB设备连接意图没有触发。我必须说,我在我的xoom(tiamat)上使用了自定义内核。我以前认为这可能与问题有关,所以我回滚到了原始的3.1版本。但是仍然没有进展。这是一段时间之前的事情,在我尝试枚举之前,所以现在我将再次回滚并继续使用原始版本,直到确定内核不是问题为止。我会在找到更多信息后回来报告成功或失败。当然,如果有人比我更好地理解这个问题,请加入讨论。 最后一个注意事项是,当我在文档中看到OTG主机模式时,我有些担心。请注意,代码完全相同,尽管引用了两种枚举方法。这可能只是一个撰写错误,但考虑到所有这些失败,仍然令人担忧。


1

连接USB键盘时,不会触发USB_DEVICE_ATTACHED。相反,系统将触发Intent.ACTION_CONFIGURATION_CHANGED。但是,由于存在配置更改,系统将重新启动Activity。您无法在重新启动的Activity中捕获操作。在这种情况下,您需要在Android清单中添加android:configChanges="keyboard|keyboardHidden",以便在连接外部键盘时不会重新启动Activity。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接