Android(安卓)USB通信弹出权限确认框
介绍
Android usb通信有两种模式:Host Mode和Accessory Mode;二者差异之处在于Host模式下,Android设备供电给usb设备;Accessory模式下usb设备供电给Android设备;官方示意图如下:
获取权限原理
1.调用UsbManager.requestPermission(UsbDevice device, PendingIntentpi)方法
/** * Requests temporary permission for the given package to access the device. * This may result in a system dialog being displayed to the user * if permission had not already been granted. * Success or failure is returned via the {@link android.app.PendingIntent} pi. * If successful, this grants the caller permission to access the device only * until the device is disconnected. * * The following extras will be added to pi: * * - {@link #EXTRA_DEVICE} containing the device passed into this call *
- {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether * permission was granted by the user *
* * @param device to request permissions for * @param pi PendingIntent for returning result */ public void requestPermission(UsbDevice device, PendingIntent pi) { try { mService.requestDevicePermission(device, mContext.getPackageName(), pi); } catch (RemoteException e) { Log.e(TAG, "RemoteException in requestPermission", e); } }
在requestPermission方法里又调用了
mService.requestDevicePermission(device, mContext.getPackageName(), pi);
mService又是什么呢?看代码:
private final IUsbManager mService;
IUsbManager位于源码frameworks\base\core\java\android\hardware\usb目录下:
我们看到,IUsbManager文件是.aidl格式的,所以这里肯定使用了进程间通信。所以使用source insight全局搜索
IUsbManager字段,搜索结果如下:
了解过aidl的同学应该都知道,aidl是一种进程间通信的语言,包含客户端和服务端两个部分。一一查看各个类发现
UsbService代码如下:
public class UsbService extends IUsbManager.Stub { private static final String TAG = "UsbService"; private final Context mContext; private UsbDeviceManager mDeviceManager; private UsbHostManager mHostManager; private final Object mLock = new Object(); /** Map from {@link UserHandle} to {@link UsbSettingsManager} */ @GuardedBy("mLock") private final SparseArray mSettingsByUser = new SparseArray(); private UsbSettingsManager getSettingsForUser(int userId) { synchronized (mLock) { UsbSettingsManager settings = mSettingsByUser.get(userId); if (settings == null) { settings = new UsbSettingsManager(mContext, new UserHandle(userId)); mSettingsByUser.put(userId, settings); } return settings; } } public UsbService(Context context) { mContext = context; final PackageManager pm = mContext.getPackageManager(); if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) { mHostManager = new UsbHostManager(context); } if (new File("/sys/class/android_usb").exists()) { mDeviceManager = new UsbDeviceManager(context); } setCurrentUser(UserHandle.USER_OWNER); final IntentFilter userFilter = new IntentFilter(); userFilter.addAction(Intent.ACTION_USER_SWITCHED); userFilter.addAction(Intent.ACTION_USER_STOPPED); mContext.registerReceiver(mUserReceiver, userFilter, null, null); } private BroadcastReceiver mUserReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); final String action = intent.getAction(); if (Intent.ACTION_USER_SWITCHED.equals(action)) { setCurrentUser(userId); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { synchronized (mLock) { mSettingsByUser.remove(userId); } } } }; private void setCurrentUser(int userId) { final UsbSettingsManager userSettings = getSettingsForUser(userId); if (mHostManager != null) { mHostManager.setCurrentSettings(userSettings); } if (mDeviceManager != null) { mDeviceManager.setCurrentSettings(userSettings); } } public void systemReady() { if (mDeviceManager != null) { mDeviceManager.systemReady(); } if (mHostManager != null) { mHostManager.systemReady(); } } /* Returns a list of all currently attached USB devices (host mdoe) */ @Override public void getDeviceList(Bundle devices) { if (mHostManager != null) { mHostManager.getDeviceList(devices); } } /* Opens the specified USB device (host mode) */ @Override public ParcelFileDescriptor openDevice(String deviceName) { if (mHostManager != null) { return mHostManager.openDevice(deviceName); } else { return null; } } /* returns the currently attached USB accessory (device mode) */ @Override public UsbAccessory getCurrentAccessory() { if (mDeviceManager != null) { return mDeviceManager.getCurrentAccessory(); } else { return null; } } /* opens the currently attached USB accessory (device mode) */ @Override public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { if (mDeviceManager != null) { return mDeviceManager.openAccessory(accessory); } else { return null; } } @Override public void setDevicePackage(UsbDevice device, String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); getSettingsForUser(userId).setDevicePackage(device, packageName); } @Override public void setAccessoryPackage(UsbAccessory accessory, String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); getSettingsForUser(userId).setAccessoryPackage(accessory, packageName); } @Override public boolean hasDevicePermission(UsbDevice device) { final int userId = UserHandle.getCallingUserId(); return getSettingsForUser(userId).hasPermission(device); } @Override public boolean hasAccessoryPermission(UsbAccessory accessory) { final int userId = UserHandle.getCallingUserId(); return getSettingsForUser(userId).hasPermission(accessory); } @Override public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) { final int userId = UserHandle.getCallingUserId(); getSettingsForUser(userId).requestPermission(device, packageName, pi); } @Override public void requestAccessoryPermission( UsbAccessory accessory, String packageName, PendingIntent pi) { final int userId = UserHandle.getCallingUserId(); getSettingsForUser(userId).requestPermission(accessory, packageName, pi); } @Override public void grantDevicePermission(UsbDevice device, int uid) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); final int userId = UserHandle.getUserId(uid); getSettingsForUser(userId).grantDevicePermission(device, uid); } @Override public void grantAccessoryPermission(UsbAccessory accessory, int uid) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); final int userId = UserHandle.getUserId(uid); getSettingsForUser(userId).grantAccessoryPermission(accessory, uid); } @Override public boolean hasDefaults(String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); return getSettingsForUser(userId).hasDefaults(packageName); } @Override public void clearDefaults(String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); getSettingsForUser(userId).clearDefaults(packageName); } @Override public void setCurrentFunction(String function, boolean makeDefault) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); if (mDeviceManager != null) { mDeviceManager.setCurrentFunctions(function, makeDefault); } else { throw new IllegalStateException("USB device mode not supported"); } } @Override public void setMassStorageBackingFile(String path) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); if (mDeviceManager != null) { mDeviceManager.setMassStorageBackingFile(path); } else { throw new IllegalStateException("USB device mode not supported"); } } @Override public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey); } @Override public void denyUsbDebugging() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); mDeviceManager.denyUsbDebugging(); } @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println("USB Manager State:"); if (mDeviceManager != null) { mDeviceManager.dump(fd, pw); } if (mHostManager != null) { mHostManager.dump(fd, pw); } synchronized (mLock) { for (int i = 0; i < mSettingsByUser.size(); i++) { final int userId = mSettingsByUser.keyAt(i); final UsbSettingsManager settings = mSettingsByUser.valueAt(i); pw.increaseIndent(); pw.println("Settings for user " + userId + ":"); settings.dump(fd, pw); pw.decreaseIndent(); } } pw.decreaseIndent(); }}
所以aidl通信的服务端是UsbService,查看requestDevicePermission方法:
@Override public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) { final int userId = UserHandle.getCallingUserId(); getSettingsForUser(userId).requestPermission(device, packageName, pi); }
我们继续查找getSettingsForUser方法:
private UsbSettingsManager getSettingsForUser(int userId) { synchronized (mLock) { UsbSettingsManager settings = mSettingsByUser.get(userId); if (settings == null) { settings = new UsbSettingsManager(mContext, new UserHandle(userId)); mSettingsByUser.put(userId, settings); } return settings; } }
这个方法返回了一个UsbSettingsManager实例,跳转到这个类中,找到requestPermission方法:
public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) { Intent intent = new Intent(); // respond immediately if permission has already been granted if (hasPermission(device)) { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); try { pi.send(mUserContext, 0, intent); } catch (PendingIntent.CanceledException e) { if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); } return; } // start UsbPermissionActivity so user can choose an activity intent.putExtra(UsbManager.EXTRA_DEVICE, device); requestPermissionDialog(intent, packageName, pi); }
首先调用hasPermission方法,判断该应用是否已经获得usb权限
public boolean hasPermission(UsbDevice device) { synchronized (mLock) { SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); if (uidList == null) { return false; } return uidList.get(Binder.getCallingUid()); } }
SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());查询获得了该usb设备访问 权限的应用列表,如果uidList为空,说明当前没有应用获得该usb设备的权限,否则调用get方法
/** * Gets the boolean mapped from the specified key, or false
* if no such mapping has been made. */ public boolean get(int key) { return get(key, false); }
执行完if(hasPermission)判断后,紧接着调用了requestPermissionDialog(intent, packageName, pi);方法
private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) { final int uid = Binder.getCallingUid(); // compare uid with packageName to foil apps pretending to be someone else try { ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0); if (aInfo.uid != uid) { throw new IllegalArgumentException("package " + packageName + " does not match caller's uid " + uid); } } catch (PackageManager.NameNotFoundException e) { throw new IllegalArgumentException("package " + packageName + " not found"); } long identity = Binder.clearCallingIdentity(); intent.setClassName("com.android.systemui", "com.android.systemui.usb.UsbPermissionActivity"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(Intent.EXTRA_INTENT, pi); intent.putExtra("package", packageName); intent.putExtra(Intent.EXTRA_UID, uid); try { mUserContext.startActivityAsUser(intent, mUser); } catch (ActivityNotFoundException e) { Slog.e(TAG, "unable to start UsbPermissionActivity"); } finally { Binder.restoreCallingIdentity(identity); } }
这个方法主要是跳转到UsbPermissionActivity中,UsbPermissionActivity就是系统弹出的权限确认框,效果图如下 所以aidl通信的服务端是UsbService,查看requestDevicePermission方法:SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
代码如下:
/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.android.systemui.usb;import android.app.Activity;import android.app.AlertDialog;import android.app.PendingIntent;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.pm.ApplicationInfo;import android.content.pm.PackageManager;import android.hardware.usb.IUsbManager;import android.hardware.usb.UsbDevice;import android.hardware.usb.UsbAccessory;import android.hardware.usb.UsbManager;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.os.ServiceManager;import android.os.UserHandle;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.widget.CheckBox;import android.widget.CompoundButton;import android.widget.TextView;import com.android.internal.app.AlertActivity;import com.android.internal.app.AlertController;import com.android.systemui.R;public class UsbPermissionActivity extends AlertActivity implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener { private static final String TAG = "UsbPermissionActivity"; private CheckBox mAlwaysUse; private TextView mClearDefaultHint; private UsbDevice mDevice; private UsbAccessory mAccessory; private PendingIntent mPendingIntent; private String mPackageName; private int mUid; private boolean mPermissionGranted; private UsbDisconnectedReceiver mDisconnectedReceiver; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); Intent intent = getIntent(); mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT); mUid = intent.getIntExtra(Intent.EXTRA_UID, -1); mPackageName = intent.getStringExtra("package"); PackageManager packageManager = getPackageManager(); ApplicationInfo aInfo; try { aInfo = packageManager.getApplicationInfo(mPackageName, 0); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "unable to look up package name", e); finish(); return; } String appName = aInfo.loadLabel(packageManager).toString(); final AlertController.AlertParams ap = mAlertParams; ap.mIcon = aInfo.loadIcon(packageManager); ap.mTitle = appName; if (mDevice == null) { ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); } else { ap.mMessage = getString(R.string.usb_device_permission_prompt, appName); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); } ap.mPositiveButtonText = getString(android.R.string.ok); ap.mNegativeButtonText = getString(android.R.string.cancel); ap.mPositiveButtonListener = this; ap.mNegativeButtonListener = this; LayoutInflater inflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null); mAlwaysUse = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse); if (mDevice == null) { mAlwaysUse.setText(R.string.always_use_accessory); } else { mAlwaysUse.setText(R.string.always_use_device); } mAlwaysUse.setOnCheckedChangeListener(this); mClearDefaultHint = (TextView)ap.mView.findViewById( com.android.internal.R.id.clearDefaultHint); mClearDefaultHint.setVisibility(View.GONE); setupAlert(); } @Override public void onDestroy() { IBinder b = ServiceManager.getService(USB_SERVICE); IUsbManager service = IUsbManager.Stub.asInterface(b); // send response via pending intent Intent intent = new Intent(); try { if (mDevice != null) { intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice); if (mPermissionGranted) { service.grantDevicePermission(mDevice, mUid); // if (mAlwaysUse.isChecked()) { final int userId = UserHandle.getUserId(mUid); service.setDevicePackage(mDevice, mPackageName, userId); } } } if (mAccessory != null) { intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory); if (mPermissionGranted) { service.grantAccessoryPermission(mAccessory, mUid); // if (mAlwaysUse.isChecked()) { final int userId = UserHandle.getUserId(mUid); service.setAccessoryPackage(mAccessory, mPackageName, userId); } } } intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted); mPendingIntent.send(this, 0, intent); } catch (PendingIntent.CanceledException e) { Log.w(TAG, "PendingIntent was cancelled"); } catch (RemoteException e) { Log.e(TAG, "IUsbService connection failed", e); } if (mDisconnectedReceiver != null) { unregisterReceiver(mDisconnectedReceiver); } super.onDestroy(); } public void onClick(DialogInterface dialog, int which) { if (which == AlertDialog.BUTTON_POSITIVE) { mPermissionGranted = true; } finish(); } public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (mClearDefaultHint == null) return; if(isChecked) { mClearDefaultHint.setVisibility(View.VISIBLE); } else { mClearDefaultHint.setVisibility(View.GONE); } }}
在onCreate方法里弹出权限确认框,如果用户点击“确定”,则mPermissionGranted = true;然后调用finish, 进入onDestroy方法调了 service.grantDevicePermission(mDevice, mUid);我们再继续进入UsbService中查看
grantDevicePermission方法
@Override public void grantDevicePermission(UsbDevice device, int uid) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); final int userId = UserHandle.getUserId(uid); getSettingsForUser(userId).grantDevicePermission(device, uid); }
getSettingForUser方法返回的是USBSettingManager实例,我们进入该类中,查找grantDevicePermission方法: public void grantDevicePermission(UsbDevice device, int uid) { synchronized (mLock) { String deviceName = device.getDeviceName(); SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); if (uidList == null) { uidList = new SparseBooleanArray(1); mDevicePermissionMap.put(deviceName, uidList); } uidList.put(uid, true); } }
代码的主要功能是将应用的uid和usb设备建立映射关系,然后存储起来。还记得上面的hasPermission方法吗 public boolean hasPermission(UsbDevice device) { synchronized (mLock) { SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); if (uidList == null) { return false; } return uidList.get(Binder.getCallingUid()); } }
这两个方法刚好一个是存uid和usb的映射关系,一个是读取uid和usb的映射关系,判断是否该uid对应的应用已具有 该usb设备的权限。
以上就是usb权限申请的逻辑,总结一下如下:
1.UsbManager.requestPermission(UsbDevice device, PendingIntent pi),在此方法中调用2
2.UsbService.requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi),在此方法中调用3
3.UsbSettingManager.requestPermission(UsbDevice device, String packageName, PendingIntent pi),在此方法中启动UsbPermissionActivity
4.UsbPermissionActivity,在此方法中
5.UsbService.grantDevicePermission(UsbDevice device, int uid)
5.UsbSettingManager.grantDevicePermission(UsbDevice device, int uid)
如果应用想要自动获取usb权限,只要在UsbPermissionActivity中处理就可以了。
更多相关文章
- Android(安卓)获取麦克风音量
- Android(安卓)图文数据JSON解析,金山词霸每日一句API的调用
- Android(安卓)user defined service handling
- 4412开发板Android和LinuxQT烧写方法
- Android在线热更新hot fix - AndFix试用
- Android中ImageButton自定义按钮的按下效果的代码实现方法,附网上
- Android(安卓)adb devices显示no permission
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用