最近在看Android USB主从设备通讯(下载Demo)相关的东西,调试时每次运行都弹下面的框,即使勾选“默认情况下用于该USB设备”,还是会弹出,在调试阶段频繁弹框影响开发效率。
下面分享下怎么屏蔽USB权限框:
1. 做过相关开发的都见过下面代码
// Check whether we have permission to access the device.
if (!mUsbManager.hasPermission(device)) {
Intent intent = new Intent(ACTION_USB_DEVICE_PERMISSION);
intent.setPackage(getPackageName());
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
intent, PendingIntent.FLAG_ONE_SHOT);
mUsbManager.requestPermission(device, pendingIntent);
return;
}
意思在没有权限的情况下,请求权限才弹的框。
2. 分析弹框流程
mUsbManager.requestPermission(UsbDevice device...);//以UsbDevice为例,UsbAccessory同理
a. frameworks/base/core/java/android/hardware/usb/UsbManager.java=>requestPermission
public void requestPermission(UsbDevice device, PendingIntent pi) {
try {
mService.requestDevicePermission(device, mContext.getPackageName(), pi);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
b. frameworks/base/core/java/android/hardware/usb/UsbDevice.java=>requestDevicePermission
@Override
public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) {
final int userId = UserHandle.getCallingUserId();
getSettingsForUser(userId).requestPermission(device, packageName, pi);
}
getSettingsForUser是
UsbDevice成员函数
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;
}
}
c. frameworks/base/services/java/com/android/server/usb/UsbSettingsManager.java=>
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);
}
接着调成员函数requestPermissionDialog
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);
}
}
由此看最终显示框的地方在SystemUI的UsbPermissionActivity。
3. 屏蔽弹框
a. 看到有博客说在UsbPermissionActivity的onCreate显示Dialog(setupAlert)时替换点击确定的代码
相当于自动点击确定
@Override
public void onCreate(Bundle icicle) {
...
//setupAlert();
mPermissionGranted = true;
finish();
}
或
@Override
public void onCreate(Bundle icicle) {
...
mAlwaysUse.setOnCheckedChangeListener(this);
mAlwaysUse.setChecked(true);
...
setupAlert();
onClick(this, AlertDialog.BUTTON_POSITIVE);
}
上述两种方法虽然能达到自动授权,个人觉得这种屏蔽方法不理想,下面介绍更好方法。
b. 直接让mUsbManager.hasPermission返回true(自动授权),不更彻底?!
下面列下函数调用关系
①. frameworks/base/core/java/android/hardware/usb/UsbManager.java=>hasPermission
public boolean hasPermission(UsbDevice device) {
try {
return mService.hasDevicePermission(device);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
②.
frameworks/base/core/java/android/hardware/usb/UsbDevice.java
=>hasDevicePermission
public boolean hasDevicePermission(UsbDevice device) {
final int userId = UserHandle.getCallingUserId();
return getSettingsForUser(userId).hasPermission(device);
}
③.
frameworks/base/services/java/com/android/server/usb/UsbSettingsManager.java=>hasPermission
// Android L(5.x)及之后的版本
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);
}
}
让条件(uid == Process.SYSTEM_UID || mDisablePermissionDialogs)为真即可
第一种方式:在AndroidManifest.xml里加上android:sharedUserId="android.uid.system",但是apk需打系统签名
第二种方式:让mDisablePermissionDialogs为真
mDisablePermissionDialogs = context.getResources().getBoolean(
com.android.internal.R.bool.config_disableUsbPermissionDialogs);
frameworks/base/core/res/res/values/config.xml
true
哈哈,看看上面注释,这么改会引起CTS fail,这就看你的项目是否要过CTS了
// Android KK(4.4)及之前的版本
public boolean hasPermission(UsbDevice device) {
synchronized (mLock) {
int uid = Binder.getCallingUid();
if (uid == Process.SYSTEM_UID) {
return true;
}
SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
if (uidList == null) {
return false;
}
return uidList.get(uid);
}
}
这里就用上述第一种方式即可。
感觉自己好啰嗦哦(贴了不少代码),对你有帮助就好。