Android-x86-6.0定製之路 - 繞過USB權限彈窗實現靜默授權

需求確立

描述

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 權限彈窗實現靜默授權這裏就修改了源碼,也就那兩行代碼的,簡單記錄下吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章