介绍

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中处理就可以了。

              
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          


更多相关文章

  1. Android(安卓)获取麦克风音量
  2. Android(安卓)图文数据JSON解析,金山词霸每日一句API的调用
  3. Android(安卓)user defined service handling
  4. 4412开发板Android和LinuxQT烧写方法
  5. Android在线热更新hot fix - AndFix试用
  6. Android中ImageButton自定义按钮的按下效果的代码实现方法,附网上
  7. Android(安卓)adb devices显示no permission
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Linux中Jenkins+Git+Gradle自动化打包And
  2. 3月份工作回顾
  3. 建议SQLite操作使用rawQuery方法
  4. [译]Android(安卓)SDK中关于View绘制流程
  5. 分享一个遍历当前文件夹下所以子目录,并在
  6. Android和iOS应用都会大量收集用户数据
  7. Android提高第八篇之SQLite分页读取[转]
  8. Android应用程序打包时,出现错误:"XXX" is
  9. android 文件下载(一)
  10. Android(安卓)并发二三事之Java线程池