绕过android usb host permision(权限)确认对话框
近段时间android开发中遇到一个难题,按照之前的《
+++但是当每一次自己的android应用运行之后都会弹出确认权限对话框。+++
Allow the app "MyAPP" to access the USB device ? [checkmark]Use by default for this USB device Cancel OK
弹出对话框的原因是因为requestPermission函数。官方有详细说明:
when you call requestPermission(). The call to requestPermission() displays a dialog to the user asking for permission to connect to the device.每次都弹出该对话框用户体验极差,也没人愿意每次开机都去给该设备授权。可否绕过该对话框呢?通过分析android源码可知:当你点击OK按钮的时候做了三个动作分别是:
intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice); IBinder b = ServiceManager.getService(USB_SERVICE); IUsbManager service = IUsbManager.Stub.asInterface(b); service.grantDevicePermission(mDevice, mUid); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted);
具体的分析就自己看android的源码(UsbPermissionActivity.java);
可是以下这三句话在上层应用中是没有办法用的。因为:ServiceManager
is android.os.ServiceManager
and thus isn't a public API.
IBinder b = ServiceManager.getService(USB_SERVICE); IUsbManager service = IUsbManager.Stub.asInterface(b); service.grantDevicePermission(mDevice, uid);不绕弯子了,以下是我的解决方案,也是参考了大量的资料。再次感谢那些有共享精神的技术牛人。所有的解决方案还是围绕着那三句话。其中最后一句中的mDevice即使你要添加权限的USB设备,uid是你的应用程序的id号
final PackageManager pm = getPackageManager(); ApplicationInfo aInfo = pm.getApplicationInfo(getPackageName(),0);
1.唯一的要求就是你的应用是系统级应用,将应用拷贝在/system/app下面并且保证设备已经ROOT并且没有问题。
2.在你的工程中增加包名为android.hardware.usb的包,并且添加IUsbManager.java文件。
package android.hardware.usb; public interface IUsbManager extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements android.hardware.usb.IUsbManager { /** Construct the stub at attach it to the interface. */ public Stub() { throw new RuntimeException("Stub!"); } /** * Cast an IBinder object into an android.hardware.usb.IUsbManager * interface, generating a proxy if needed. */ public static android.hardware.usb.IUsbManager asInterface( android.os.IBinder obj) { throw new RuntimeException("Stub!"); } public android.os.IBinder asBinder() { throw new RuntimeException("Stub!"); } public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { throw new RuntimeException("Stub!"); } static final int TRANSACTION_getDeviceList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_openDevice = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_getCurrentAccessory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_openAccessory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); static final int TRANSACTION_setDevicePackage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4); static final int TRANSACTION_setAccessoryPackage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5); static final int TRANSACTION_hasDevicePermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6); static final int TRANSACTION_hasAccessoryPermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7); static final int TRANSACTION_requestDevicePermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8); static final int TRANSACTION_requestAccessoryPermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9); static final int TRANSACTION_grantDevicePermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 10); static final int TRANSACTION_grantAccessoryPermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 11); static final int TRANSACTION_hasDefaults = (android.os.IBinder.FIRST_CALL_TRANSACTION + 12); static final int TRANSACTION_clearDefaults = (android.os.IBinder.FIRST_CALL_TRANSACTION + 13); static final int TRANSACTION_setCurrentFunction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 14); static final int TRANSACTION_setMassStorageBackingFile = (android.os.IBinder.FIRST_CALL_TRANSACTION + 15); } /* Returns a list of all currently attached USB devices */ public void getDeviceList(android.os.Bundle devices) throws android.os.RemoteException; /* * Returns a file descriptor for communicating with the USB device. The * native fd can be passed to usb_device_new() in libusbhost. */ public android.os.ParcelFileDescriptor openDevice( java.lang.String deviceName) throws android.os.RemoteException; /* Returns the currently attached USB accessory */ public android.hardware.usb.UsbAccessory getCurrentAccessory() throws android.os.RemoteException; /* * Returns a file descriptor for communicating with the USB accessory. This * file descriptor can be used with standard Java file operations. */ public android.os.ParcelFileDescriptor openAccessory( android.hardware.usb.UsbAccessory accessory) throws android.os.RemoteException; /* * Sets the default package for a USB device (or clears it if the package * name is null) */ public void setDevicePackage(android.hardware.usb.UsbDevice device, java.lang.String packageName) throws android.os.RemoteException; /* * Sets the default package for a USB accessory (or clears it if the package * name is null) */ public void setAccessoryPackage( android.hardware.usb.UsbAccessory accessory, java.lang.String packageName) throws android.os.RemoteException; /* Returns true if the caller has permission to access the device. */ public boolean hasDevicePermission(android.hardware.usb.UsbDevice device) throws android.os.RemoteException; /* Returns true if the caller has permission to access the accessory. */ public boolean hasAccessoryPermission( android.hardware.usb.UsbAccessory accessory) throws android.os.RemoteException; /* * Requests permission for the given package to access the device. Will * display a system dialog to query the user if permission had not already * been given. */ public void requestDevicePermission(android.hardware.usb.UsbDevice device, java.lang.String packageName, android.app.PendingIntent pi) throws android.os.RemoteException; /* * Requests permission for the given package to access the accessory. Will * display a system dialog to query the user if permission had not already * been given. Result is returned via pi. */ public void requestAccessoryPermission( android.hardware.usb.UsbAccessory accessory, java.lang.String packageName, android.app.PendingIntent pi) throws android.os.RemoteException; /* Grants permission for the given UID to access the device */ public void grantDevicePermission(android.hardware.usb.UsbDevice device, int uid) throws android.os.RemoteException; /* Grants permission for the given UID to access the accessory */ public void grantAccessoryPermission( android.hardware.usb.UsbAccessory accessory, int uid) throws android.os.RemoteException; /* * Returns true if the USB manager has default preferences or permissions * for the package */ public boolean hasDefaults(java.lang.String packageName) throws android.os.RemoteException; /* Clears default preferences and permissions for the package */ public void clearDefaults(java.lang.String packageName) throws android.os.RemoteException; /* Sets the current USB function. */ public void setCurrentFunction(java.lang.String function, boolean makeDefault) throws android.os.RemoteException; /* Sets the file path for USB mass storage backing file. */ public void setMassStorageBackingFile(java.lang.String path) throws android.os.RemoteException; }
3.在增加包名为android.os的包,并且添加ServiceManager.java文件
package android.os; import java.util.Map; public final class ServiceManager { public static IBinder getService(String name) { throw new RuntimeException("Stub!"); } /** * Place a new @a service called @a name into the service manager. * * @param name * the name of the new service * @param service * the service object */ public static void addService(String name, IBinder service) { throw new RuntimeException("Stub!"); } /** * Retrieve an existing service called @a name from the service manager. * Non-blocking. */ public static IBinder checkService(String name) { throw new RuntimeException("Stub!"); } public static String[] listServices() throws RemoteException { throw new RuntimeException("Stub!"); } /** * This is only intended to be called when the process is first being * brought up and bound by the activity manager. There is only one thread in * the process at that time, so no locking is done. * * @param cache * the cache of service references * @hide */ public static void initServiceCache(Map<String, IBinder> cache) { throw new RuntimeException("Stub!"); } }
最后还得在你的工程中添加如下的权限:
<uses-permission android:name="android.permission.MANAGE_USB" />
eclipse如果报错直接Clean就行了,因为加上这句话后会提醒你需要应用得到system授权。忽略直接clean,如果有报错的clean就行了,不必惊慌....
附:
#adb shell # mount -o remount,rw -t ext4 /dev/block/mtdblock3 /system // 让分区可写。 //windows回到命令提示符下 >adb push xxx.apk /system/app/
我的代码片段关于绕开确认对话框:
// Register receiver for USB permission mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent( ACTION_USB_PERMISSION), 0); Intent intent = new Intent(); intent.setAction(ACTION_USB_PERMISSION); IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_USB_PERMISSION); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); registerReceiver(mReceiver, filter); // Request permission for (UsbDevice device : mManager.getDeviceList().values()) { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); final PackageManager pm = getPackageManager(); try { ApplicationInfo aInfo = pm.getApplicationInfo(getPackageName(), 0); try { IBinder b = ServiceManager.getService(USB_SERVICE); IUsbManager service = IUsbManager.Stub.asInterface(b); service.grantDevicePermission(device, aInfo.uid); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (NameNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } getAppContext().sendBroadcast(intent); // mManager.requestPermission(device, mPermissionIntent); logMsg("UsbManager.EXTRA_DEVICE 11111111111111======" + mManager.openDevice(device)); } }
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { UsbDevice device = (UsbDevice) intent .getParcelableExtra(UsbManager.EXTRA_DEVICE); logMsg("UsbManager.EXTRA_DEVICE 22222222222222222 ========" + intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)); logMsg("是否有权限了?????? " + mManager.hasPermission(device)); if (intent.getBooleanExtra( UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if (device != null) { // Open reader logMsg("Opening reader: " + device.getDeviceName() + "..."); new OpenTask().execute(device); } } else { if (device != null) { logMsg("Permission no EXTRA_PERMISSION_GRANTED for device " + device.getDeviceName()); } } } } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { synchronized (this) { // Close reader /* logMsg("Closing reader..."); */ new CloseTask().execute(); } } } };
这样做就可以绕开该对话框。唯一可能麻烦的就是你的应用得弄成系统级应用,如果没有办法root就没招了!
希望大家有更好的方法....