深度解决 SecurityException: User has not given permission to device UsbDevice

深度解决 SecurityException: User has not given permission to device UsbDevice

在Android上直接使用USB端口会出现如下权限相关错误:

UsbManager: exception in UsbManager.openDevice
java.lang.SecurityException: User has not given permission to device UsbDevice[mName=/dev/bus/usb/001/017,mVendorId=1208,mProductId=7,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=EPSON,mProductName=USB2.0 Printer (Hi-speed),mVersion=2.0,mSerialNumber=5458554B3135393861,mConfigurations=[
UsbConfiguration[mId=1,mName=null,mAttributes=192,mMaxPower=1,mInterfaces=[
UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=7,mSubclass=1,mProtocol=2,mEndpoints=[
UsbEndpoint[mAddress=4,mAttributes=2,mMaxPacketSize=512,mInterval=0]
UsbEndpoint[mAddress=133,mAttributes=2,mMaxPacketSize=512,mInterval=0]]]]
 at android.os.Parcel.readException(Parcel.java:1602)
 at android.os.Parcel.readException(Parcel.java:1555)
 at android.hardware.usb.IUsbManager$Stub$Proxy.openDevice(IUsbManager.java:418)
 at android.hardware.usb.UsbManager.openDevice(UsbManager.java:327)
 at com.example.tony.usbprinter.MainActivity.setPrinterInterface(MainActivity.java:127)
 at com.example.tony.usbprinter.MainActivity.onCreate(MainActivity.java:63)
 at android.app.Activity.performCreate(Activity.java:6367)
 at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2397)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2504)
 at android.app.ActivityThread.access$900(ActivityThread.java:165)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1368)
 at android.os.Handler.dispatchMessage(Handler.java:102)
 at android.os.Looper.loop(Looper.java:150)
 at android.app.ActivityThread.main(ActivityThread.java:5546)
 at java.lang.reflect.Method.invoke(Native Method)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)

具体是以下抛出的异常:

    /**
     * Opens the device so it can be used to send and receive
     * data using {@link android.hardware.usb.UsbRequest}.
     *
     * @param device the device to open
     * @return a {@link UsbDeviceConnection}, or {@code null} if open failed
     */
    public UsbDeviceConnection openDevice(UsbDevice device) {
        try {
            String deviceName = device.getDeviceName();
            ParcelFileDescriptor pfd = mService.openDevice(deviceName);
            if (pfd != null) {
                UsbDeviceConnection connection = new UsbDeviceConnection(device);
                boolean result = connection.open(deviceName, pfd);
                pfd.close();
                if (result) {
                    return connection;
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "exception in UsbManager.openDevice", e);
        }
        return null;
    }

一般解决方案

所以无法在应用层进行try catch这个异常。要解决这个需要申请权限,来自【已解决】android程序运行出错:UsbManager(4294): exception in UsbManager.openDevice,java.lang.SecurityException: User has not given permission to device UsbDevice.

import android.widget.Toast;
import android.content.Intent;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;

public class SomeActivity extends BaseActivity {
    private static UsbManager mUsbManager;

    private void openUsbDevice(){
        //before open usb device
        //should try to get usb permission
        tryGetUsbPermission();
    }

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

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


    private void afterGetUsbPermission(UsbDevice usbDevice){
        //call method to set up device communication
        Toast.makeText(context, String.valueOf("Got permission for usb device: " + usbDevice), Toast.LENGTH_LONG).show();
        Toast.makeText(context, String.valueOf("Found USB device: VID=" + usbDevice.getVendorId() + " PID=" + usbDevice.getProductId()), Toast.LENGTH_LONG).show();

        doYourOpenUsbDevice(usbDevice);
    }

    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
    }

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

高级解决方案

在一些场合,根本没有「用户」存在,这种使用时向用户申请权限的方式就比较麻烦,所以要从其它方面实现这个功能。
所谓高级方案就是「设法绕过USB权限弹窗」,目标明确后就需要了解一下内部Android系统对USB权限的处理是怎样的,这里基于5.0源码进行分析,通过标题进行搜索得出在platform_frameworks_base这个子仓的services/usb/java/com/android/server/usb/UsbSettingsManager.java文件中的checkPermission方法中打印出来的:

    public void checkPermission(UsbDevice device) {
        if (!hasPermission(device)) {
            throw new SecurityException("User has not given permission to device " + device);
        }
    }

追踪hasPermission()到:

    public boolean hasPermission(UsbDevice device) {
        synchronized (mLock) {
            int uid = Binder.getCallingUid();
            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
                return true;
            }
            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
            if (uidList == null) {
                return false;
            }
            return uidList.get(uid);
        }
    }

这里有3个条件可以触发返回true,uidList不为空(简单讲就是弹窗用户进行了允许);uid为Process.SYSTEM_UID;mDisablePermissionDialogs为true。只能从后两者中找出突破口。

方案1:uidList不为空(简单讲就是弹窗用户进行了允许)
这个为上述的一般方案,这里不再做讨论。
方案2:uid为Process.SYSTEM_UID
APP签上系统签名,使用system_uid即可。
方案3: mDisablePermissionDialogs为true
**注:**该方案需要重新编译Android系统源码,需要有一定源码编译经验才能做到。
因为这个变量读取的是 com.android.internal.R.bool.config_disableUsbPermissionDialogs变量值,该值为于 frameworks/base/core/res/res/values/config.xml中,属于framworks-res.apk这个系统资源程序。,,
# 1. 将`config_disableUsbPermissionDialogs`由`false`改为`true`

# 2. 重新编译framworks-res.apk程序
mmm  frameworks/base/core/res/

# 3. 替换系统framworks-res.apk
adb push out/target/product/nanopi2/system/framework/framework-res.apk /system/framework/

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁保康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值