需求確立
描述
1. 我們的 App 要求接入多個外部設備,像打印機、掃碼器、讀卡器和 POS 機等,其中打印機和掃碼器就是使用 USB 連接的。遇到的問題是,我先接入打印機,再接入掃碼器,發現打印機就連接不了,必須重新插拔下才行,但是單獨連接打印機或者掃碼器都是正常的。
後來谷歌找到相關問題:
假如有兩個 USB 設備 A 和 B,先接入 A,再接入 B,發現在獲取設備 A 的 UsbInterface 的時候,是空的,單接 A 或 B 都是可以正常工作的。
2. 首次插入 USB 設備時,會出現權限請求彈窗,不進行同意操作的話,USB 設備就識別不了,現在要求去掉權限請求彈窗,實現靜默授權。
確認
簡單整理:
- 同時識別多個 USB 設備
- USB 設備靜默授權
功能實現
同時識別多個 USB 設備
找到frameworks/base/services/usb/java/com/android/server/usb/UsbHostManager.java
文件
調試分析,識別不了 USB 設備的原因指向addUsbConfiguration
方法:
private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
if (mNewConfiguration != null) { // 這裏mNewConfiguration指向上一個USB設備的Configuration,然後被清理了
mNewConfiguration.setInterfaces(
mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
mNewInterfaces.clear();
}
mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
mNewConfigurations.add(mNewConfiguration);
}
解決此問題,endUsbDeviceAdded
方法把 mNewConfiguration 和 mNewInterface 賦值爲 null,以後執行addConfiguration
方法時會針對新設備去新建這些對象,具體修改如下:
private void endUsbDeviceAdded() {
if (DEBUG) {
Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
}
if (mNewInterface != null) {
mNewInterface.setEndpoints(
mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
}
if (mNewConfiguration != null) {
mNewConfiguration.setInterfaces(
mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
}
synchronized (mLock) {
if (mNewDevice != null) {
mNewDevice.setConfigurations(
mNewConfigurations.toArray(new UsbConfiguration[mNewConfigurations.size()]));
mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
Slog.d(TAG, "Added device " + mNewDevice);
getCurrentSettings().deviceAttached(mNewDevice);
mUsbAlsaManager.usbDeviceAdded(mNewDevice);
} else {
Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
}
mNewDevice = null;
mNewConfigurations = null;
mNewInterfaces = null;
mNewEndpoints = null;
mNewConfiguration = null; // 添加
mNewInterface = null; // 添加
}
}
USB 設備靜默授權
實現 USB 設備靜默授權的話挺簡單的,找到frameworks/base/packages/SystemUI/src/com/android/systemui/usb /UsbPermissionActivity.java
文件,定位onCreate
方法,
用
mPermissionGranted = true;
finish();
替換
setupAlert();
這樣就去掉彈窗,實現靜默授權了, 具體修改如下:
@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;
// add "always use" checkbox
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);
// begin
// setupAlert();
mPermissionGranted = true;
finish();
// end
}
小結
在 Android-6.0 上,關於同時識別多個 USB 設備的 Bug 已經被官方修復了,這裏還是講下,算是作個筆記吧。至於如何去掉 USB 權限彈窗實現靜默授權這裏就修改了源碼,也就那兩行代碼的,簡單記錄下吧