最近一直在给自己做减法,所以将之前保存的书签和未记录的成长一起整理 - 归纳 - 输出,此处主要记录了在开发售货机app时,因usb权限未开启,从而导致硬件设备的打印功能(USB OTG 通信)无法调用的场景处理。


  • 所遇场景
  • 清单授权
  • 动态授权
  • 兴趣扩展


如果你能看到这篇blog,你可能大概率遇到了USB相关的功能,虽然不一定能解决你的问题,但 可以把它当作一切的开始...

usb权限配置主要有以下俩种方式

  • AndroidMainfest 清单文件 - 静态授权
  • 代码中动态授权 - 6.0之后记得动态权限适配

所遇场景

如果不是因为在售货机中遇到了到了下面usb相关代码的话,我平时开发应该很少能接触到usb ~

/**
    * 我看了一下这个方法,整体是判断某个设备的usb是否赋予权限,未授权的话就进行权限申请
    * */
    public void usbPermiss(Context context) {
        UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        // 获取设备
        HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
        if (deviceList.size() > 0) {
            Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
            // 这里是if不是while,说明我只想支持一种device
            while (deviceIterator.hasNext()) {
                final UsbDevice device = deviceIterator.next();
                //Toast.makeText( this, device.toString(), Toast.LENGTH_SHORT).show();
                // 官方文档上边是这样写的,直接获取第一个,但往往不一定只连接一个设备,就要求我们找到自己想要的那个,一般的做法是
                int count = device.getInterfaceCount();
                for (int i = 0; i < count; i++) {
                    UsbInterface intf = device.getInterface(i);
                    // 之后我们会根据 intf的 getInterfaceClass 判断是哪种类型的Usb设备,
                    // 并且结合 device.getVectorID() 或者厂家ID进行过滤,比如 UsbConstants.USB_CLASS_PRINTER
                    if ((device.getVendorId() == 0x4b43 && device.getProductId() == 0x3538)
                            || (device.getVendorId() == 0x0FE6 && device.getProductId() == 0x811E)) {
                        // 这个device就是你要找的UsbDevice,此时还需要进行权限判断
                        // 没有权限
                        if (!manager.hasPermission(device)) {
                            String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
                            PendingIntent mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
                            manager.requestPermission(device, mPermissionIntent);
                            return;
                        }
                    }
                }
            }
        }
    }

关于Android USB 授权方式的实现,网上都在熬老汤了,千篇一律,长得都一样,此处直接借鉴、修改,方便理解、使用 ~

清单授权

AndroidMainfest - 在对应的Activity中加入以下配置

<!-- USB -->
    <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" />
    <!-- USB END -->

示例

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rxjava">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.RxJava">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
                <meta-data
                    android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                    android:resource="@xml/device_filter" />
            </intent-filter>
        </activity>
    </application>

</manifest>

在读 device_filter 中,要了解usb设备是通过以下俩个属性来一起定义的

  • vendor-id 厂商id
  • product-id 产品id

注意:其中 device_filter 中列出了可用 usb 设备,当usb 设备连接手机之后,app 会自动询问是否允许获取该 usb 的权限。这里有一个 linux 的 usb设备厂商id 和 产品id 的汇总,可以作为 Android usb 设备的参考

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 0x0403 / 0x6001: FTDI FT232R UART -->
    <usb-device vendor-id="1027" product-id="24577" />

    <!-- 0x0403 / 0x6015: FTDI FT231X -->
    <usb-device vendor-id="1027" product-id="24597" />

    <!-- 0x2341 / Arduino -->
    <usb-device vendor-id="9025" />

    <!-- 0x16C0 / 0x0483: Teensyduino  -->
    <usb-device vendor-id="5824" product-id="1155" />

    <!-- 0x10C4 / 0xEA60: CP210x UART Bridge -->
    <usb-device vendor-id="4292" product-id="60000" />

    <!-- 0x067B / 0x2303: Prolific PL2303 -->
    <usb-device vendor-id="1659" product-id="8963" />

    <!-- 0x1a86 / 0x7523: Qinheng CH340 -->
    <usb-device vendor-id="6790" product-id="29987" />
</resources>

动态授权

USB权限申请(前提是已经定位到要申请USB权限的usbdevice)

//获取USB设备ACTION
    private static final String ACTION_USB_PERMISSION = "com.android.usb.USB_PERMISSION";

	  //获取USB设备列表及定位到要申请权限的USB设备
//    UsbManager mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
//    HashMap<String, UsbDevice> devices = mUsbManager.getDeviceList();
//    List<UsbDevice> deviceList = new ArrayList<UsbDevice>();
//    for(UsbDevice device:devices.values()){
//        //获取打印机设备 vid和pid
//        if (3540 == device.getVendorId() && 567 == device.getProductId()) {
//            currentDevice = device;
//        }
//    }

    //开始申请USB权限
    private void getUsbPermission(UsbDevice mUSBDevice) {
        UltraLog.d("开始申请USB权限");
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        mContext.registerReceiver(mUsbReceiver, filter);
        // 该代码执行后,系统弹出一个对话框/等待权限
        mUsbManager.requestPermission(mUSBDevice, pendingIntent); 

	  //以下代码是因为在系统层将弹出框直接修改掉了,可以不用
//    long start = System.currentTimeMillis();
//    while (!mUsbManager.hasPermission(mUSBDevice)) {
//        long current = System.currentTimeMillis();
//        if ((current - start) > 3500) {
//            break;
//        }
//        try {
//            Thread.sleep(50);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }
 }

USB广播接受者 - 动态注册

private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        @SuppressLint("NewApi")
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    mContext.unregisterReceiver(mUsbReceiver);
                    UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
                            && currentDevice.equals(device)) {
                        //TODO 授权成功,操作USB设备
                    } else {
                        //用户点击拒绝了
                    }
                }
            }
        }
    };

兴趣扩展

在查usb授权的时候,网上的主流版本就是上方的静态授权和动态授权了,下方也是动态授权的方式,不过走的是英语注释,我脑补了一下中文注释,在此留档,愿对你有所帮助~

/**
     * 获得 usb 权限
     */
    private void openUsbDevice() {
        //before open usb device
        //should try to get usb permission
        tryGetUsbPermission();
    }

    UsbManager mUsbManager;
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";

    //获取USB权限状态
    private void tryGetUsbPermission() {
        mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(mUsbPermissionActionReceiver, filter);
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);

        //here do emulation to ask all connected usb device for permission
        for (final UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
            //add some conditional check if necessary
            //if(isWeCaredUsbDevice(usbDevice)){
            if (mUsbManager.hasPermission(usbDevice)) {
                //if has already got permission, just goto connect it
                //that means: user has choose yes for your previously popup window asking for grant perssion for this usb device
                //and also choose option: not ask again
                //有权限后的处理方式
                afterGetUsbPermission(usbDevice);
            } else {
                //this line will let android popup window, ask user whether to allow this app to have permission to operate this usb device
                //无权限则申请权限
                mUsbManager.requestPermission(usbDevice, mPermissionIntent);
            }
            //}
        }
    }

    //有usb权限后的执行逻辑
    private void afterGetUsbPermission(UsbDevice usbDevice) {
        //call method to set up device communication
        //Toast.makeText(this, String.valueOf("Got permission for usb device: " + usbDevice), Toast.LENGTH_LONG).show();
        //Toast.makeText(this, String.valueOf("Found USB device: VID=" + usbDevice.getVendorId() + " PID=" + usbDevice.getProductId()), Toast.LENGTH_LONG).show();
        doYourOpenUsbDevice(usbDevice);
    }

    //开始对应的usb设备
    private void doYourOpenUsbDevice(UsbDevice usbDevice) {
        //now follow line will NOT show: User has not given permission to device UsbDevice
        UsbDeviceConnection connection = mUsbManager.openDevice(usbDevice);
        //add your operation code here
    }

    //usb权限监听者
    private final BroadcastReceiver mUsbPermissionActionReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    UsbDevice usbDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        //user choose YES for your previously popup window asking for grant perssion for this usb device
                        //如果有我们usb设备的话,正常执行上方的逻辑就好
                        if (null != usbDevice) {
                            afterGetUsbPermission(usbDevice);
                        }
                    } else {
                        //user choose NO for your previously popup window asking for grant perssion for this usb device
                        Toast.makeText(context, String.valueOf("Permission denied for device" + usbDevice), Toast.LENGTH_LONG).show();
                    }
                }
            }
        }
    };