最近一直在给自己做减法,所以将之前保存的书签和未记录的成长一起整理 - 归纳 - 输出,此处主要记录了在开发售货机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();
}
}
}
}
};