4

按照Android USB Host 文档中的说明,我设法通过USB_DEVICE_ATTACHED意图检测到新的 USB 设备。要将通知限制到某些设备,可以指定资源文件:

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

device_filter.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="1234" product-id="5678" />
</resources>

问题是,如果在插入 USB 设备后启动服务,则不会收到任何意图。我可以getDeviceList用来获取设备列表,但希望避免从device_filter.xml文件中复制过滤条件。那可能吗?

4

2 回答 2

3

过滤功能在 中实现frameworks/base/services/java/com/android/server/usb/UsbSettingsManager.java,但不幸的是这些是私有的。我提取了它的部分实现,它可以像这样使用:

private void scanDevices() {
    ArrayList<UsbDevice> devices;

    try {
        devices = UsbDeviceFilter.getMatchingHostDevices(this, R.xml.wifi_devices);
    } catch (Exception e) {
        Log.w(TAG, "Failed to parse devices.xml: " + e.getMessage());
        return;
    }

    for (UsbDevice device : devices) {
        Log.d(TAG, "Matched device " + device);
    }
}

目前只接受主机设备,但添加对附件设备的支持是微不足道的。

UsbDeviceFilter.xml:

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.content.Context;
import android.content.res.XmlResourceParser;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;

/**
 * Utility to test whether a USB device is accepted by a device filter. Heavily
 * based on com.android.server.usb.UsbSettingsManager.
 * @author Peter Wu <lekensteyn@gmail.com>
 */
public class UsbDeviceFilter {
    private final List<DeviceFilter> hostDeviceFilters;

    public UsbDeviceFilter(XmlPullParser parser) throws XmlPullParserException,
            IOException {
        hostDeviceFilters = new ArrayList<UsbDeviceFilter.DeviceFilter>();
        int eventType = parser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            String tagName = parser.getName();
            if ("usb-device".equals(tagName)
                    && parser.getEventType() == XmlPullParser.START_TAG) {
                hostDeviceFilters.add(DeviceFilter.read(parser));
            }
            eventType = parser.next();
        }
    }

    public boolean matchesHostDevice(UsbDevice device) {
        for (DeviceFilter filter : hostDeviceFilters) {
            if (filter.matches(device)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Get a list of connected USB Host devices matching the devices filter.
     * @param ctx A non-null application context.
     * @param resourceId The resource ID pointing to a devices filter XML file.
     * @return A list of connected host devices matching the filter. 
     * @throws XmlPullParserException
     * @throws IOException
     */
    public static ArrayList<UsbDevice> getMatchingHostDevices(Context ctx,
            int resourceId) throws XmlPullParserException, IOException {
        UsbManager usbManager = (UsbManager) ctx
                .getSystemService(Context.USB_SERVICE);
        XmlResourceParser parser = ctx.getResources().getXml(resourceId);
        UsbDeviceFilter devFilter;

        try {
            devFilter = new UsbDeviceFilter(parser);
        } finally {
            parser.close();
        }

        ArrayList<UsbDevice> matchedDevices = new ArrayList<UsbDevice>();
        for (UsbDevice device : usbManager.getDeviceList().values()) {
            if (devFilter.matchesHostDevice(device)) {
                matchedDevices.add(device);
            }
        }
        return matchedDevices;
    }

    public static class DeviceFilter {
        // USB Vendor ID (or -1 for unspecified)
        public final int mVendorId;
        // USB Product ID (or -1 for unspecified)
        public final int mProductId;
        // USB device or interface class (or -1 for unspecified)
        public final int mClass;
        // USB device subclass (or -1 for unspecified)
        public final int mSubclass;
        // USB device protocol (or -1 for unspecified)
        public final int mProtocol;

        private DeviceFilter(int vid, int pid, int clasz, int subclass,
                int protocol) {
            mVendorId = vid;
            mProductId = pid;
            mClass = clasz;
            mSubclass = subclass;
            mProtocol = protocol;
        }

        private static DeviceFilter read(XmlPullParser parser) {
            int vendorId = -1;
            int productId = -1;
            int deviceClass = -1;
            int deviceSubclass = -1;
            int deviceProtocol = -1;

            int count = parser.getAttributeCount();
            for (int i = 0; i < count; i++) {
                String name = parser.getAttributeName(i);
                // All attribute values are ints
                int value = Integer.parseInt(parser.getAttributeValue(i));

                if ("vendor-id".equals(name)) {
                    vendorId = value;
                } else if ("product-id".equals(name)) {
                    productId = value;
                } else if ("class".equals(name)) {
                    deviceClass = value;
                } else if ("subclass".equals(name)) {
                    deviceSubclass = value;
                } else if ("protocol".equals(name)) {
                    deviceProtocol = value;
                }
            }

            return new DeviceFilter(vendorId, productId, deviceClass,
                    deviceSubclass, deviceProtocol);
        }

        private boolean matches(int clasz, int subclass, int protocol) {
            return ((mClass == -1 || clasz == mClass)
                    && (mSubclass == -1 || subclass == mSubclass)
                    && (mProtocol == -1 || protocol == mProtocol));
        }

        public boolean matches(UsbDevice device) {
            if (mVendorId != -1 && device.getVendorId() != mVendorId)
                return false;
            if (mProductId != -1 && device.getProductId() != mProductId)
                return false;

            // check device class/subclass/protocol
            if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
                    device.getDeviceProtocol()))
                return true;

            // if device doesn't match, check the interfaces
            int count = device.getInterfaceCount();
            for (int i = 0; i < count; i++) {
                UsbInterface intf = device.getInterface(i);
                if (matches(intf.getInterfaceClass(),
                        intf.getInterfaceSubclass(),
                        intf.getInterfaceProtocol()))
                    return true;
            }

            return false;
        }

    }
}
于 2013-11-13T23:07:28.870 回答
0

这个解决方案对我来说非常有效:

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

device_filter.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="6790" product-id="29987" />
</resources>

供应商 ID 和产品 ID 用于通用 CH340 中文 Arduino 克隆现在权限对话框窗口永远不会弹出。

于 2018-08-16T12:39:15.310 回答