绕过android usb host permision(权限)确认对话框
近段时间android开发中遇到一个难题,按照之前的《
》博客去处理有关USB设备访问的问题。这个google android官网上有说明,原文链接中指的就是官网的说明。
+++但是当每一次自己的android应用运行之后都会弹出确认权限对话框。+++
- Allow the app "MyAPP" to access the USB device ?
- [checkmark]Use by default for this USB device
- Cancel OK
即使点击default的checkbox框在设备重启之后依然会弹出该对话框。
弹出对话框的原因是因为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就行了,不必惊慌....
附:
将APK拷贝到/system/app
的方法是:
- #adb shell
- # mount -o remount,rw -t ext4 /dev/block/mtdblock3 /system // 让分区可写。
- //windows回到命令提示符下
- >adb push xxx.apk /system/app/
我的代码片段关于绕开确认对话框:
- <span style="color:#333333">// 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 {
- </span><span style="color:#ff0000">IBinder b = ServiceManager.getService(USB_SERVICE);
- IUsbManager service = IUsbManager.Stub.asInterface(b);
- service.grantDevicePermission(device, aInfo.uid);</span><span style="color:#333333">
- } 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));
- }
- }
- </span>
广播接收器:
- 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();
- }
- }
- }
- };
通过我的代码片段大家应该已经知道了,我处理该问题时自己伪造了一个假的广播,也就是对话框OK按钮所做的工作被我提前封好了,直接发送广播让自己接收,就绕开了那个对话框。
这样做就可以绕开该对话框。唯一可能麻烦的就是你的应用得弄成系统级应用,如果没有办法root就没招了!
希望大家有更好的方法....