Use the device_filter.xml resource file to filter USB enumeration results

时间秒杀一切 提交于 2019-12-06 05:47:15

问题


Following the instructions in the Android USB Host documentation, I managed to detect new USB devices via a USB_DEVICE_ATTACHED intent. To restrict notifications to certain devices, a resource file can be specified:

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

The problem is, if the service starts after the USB device is inserted, no intent is received. I can use getDeviceList to obtain a list of devices, but wish to avoid duplicating the filter conditions from the device_filter.xml file. Is that possible?


回答1:


The filtering functionality is implemented in frameworks/base/services/java/com/android/server/usb/UsbSettingsManager.java, but these are unfortunately private. I extracted part of its implementation, it can be used like this:

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);
    }
}

Currently only host devices are accepted, but adding support for accesory devices is trivial.

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;
        }

    }
}



回答2:


This solution worked perfectly for me:

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

The vendor ID and Product ID are for generic CH340 chinese Arduino clones Now the permission dialog window never pops up.



来源:https://stackoverflow.com/questions/19965209/use-the-device-filter-xml-resource-file-to-filter-usb-enumeration-results

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!