From a4e2e2f4afd9a9678b2122c8d0e4bb02fa3f7fd3 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jul 2018 18:34:17 +0200 Subject: [PATCH] Request missing permission when rescanning USB devices --- .../UsbConnectionDispatcher.java | 77 ++++++++++++------- .../securitytoken/usb/UsbTransport.java | 16 ++-- .../keychain/ui/UsbEventReceiverActivity.java | 20 +---- .../ui/base/BaseSecurityTokenActivity.java | 14 ++-- 4 files changed, 68 insertions(+), 59 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbConnectionDispatcher.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbConnectionDispatcher.java index e2ec68101..da5702242 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbConnectionDispatcher.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/UsbConnectionDispatcher.java @@ -17,7 +17,7 @@ package org.sufficientlysecure.keychain.securitytoken; -import android.app.Activity; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -25,30 +25,37 @@ import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; +import org.sufficientlysecure.keychain.BuildConfig; +import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo.TokenType; import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransport; -import org.sufficientlysecure.keychain.ui.UsbEventReceiverActivity; import timber.log.Timber; public class UsbConnectionDispatcher { - private Activity mActivity; + private static final String ACTION_USB_PERMISSION = "org.sufficientlysecure.keychain.ui.USB_PERMISSION"; - private OnDiscoveredUsbDeviceListener mListener; - private UsbManager mUsbManager; + private Context context; + private OnDiscoveredUsbDeviceListener onDiscoveredUsbDeviceListener; + private UsbManager usbManager; - /** - * Receives broadcast when a supported USB device get permission. - */ - private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { + public UsbConnectionDispatcher(Context context, OnDiscoveredUsbDeviceListener listener) { + this.context = context.getApplicationContext(); + this.onDiscoveredUsbDeviceListener = listener; + this.usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + } + + private final BroadcastReceiver usbBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + if (action == null) { + return; + } switch (action) { - case UsbEventReceiverActivity.ACTION_USB_PERMISSION: { + case ACTION_USB_PERMISSION: { UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); - boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, - false); + boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); if (permission) { Timber.d("Got permission for " + usbDevice.getDeviceName()); sendUsbTransportDiscovered(usbDevice); @@ -59,21 +66,15 @@ public class UsbConnectionDispatcher { } }; - public UsbConnectionDispatcher(final Activity activity, final OnDiscoveredUsbDeviceListener listener) { - this.mActivity = activity; - this.mListener = listener; - this.mUsbManager = (UsbManager) activity.getSystemService(Context.USB_SERVICE); - } - public void onStart() { final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(UsbEventReceiverActivity.ACTION_USB_PERMISSION); + intentFilter.addAction(ACTION_USB_PERMISSION); - mActivity.registerReceiver(mUsbReceiver, intentFilter); + context.registerReceiver(usbBroadcastReceiver, intentFilter); } public void onStop() { - mActivity.unregisterReceiver(mUsbReceiver); + context.unregisterReceiver(usbBroadcastReceiver); } /** @@ -83,24 +84,48 @@ public class UsbConnectionDispatcher { // Note: we don't check devices VID/PID because // we check for permission instead. // We should have permission only for matching devices - for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) { - if (mUsbManager.hasPermission(usbDevice)) { + for (UsbDevice usbDevice : usbManager.getDeviceList().values()) { + Timber.d("Device: %s", usbDevice.toString()); + if (usbManager.hasPermission(usbDevice)) { + Timber.d("Got permission!"); sendUsbTransportDiscovered(usbDevice); break; } + TokenType tokenType = UsbTransport.getTokenTypeFromUsbDeviceInfo( + usbDevice.getVendorId(), usbDevice.getProductId(), null); + if (tokenType != null) { + Timber.d("Token type: %s", tokenType); + requestPermissionForUsbDevice(context, usbDevice); + break; + } + Timber.d("Unknown device type, doing nothing…"); } } private void sendUsbTransportDiscovered(UsbDevice usbDevice) { - if (mListener == null) { + if (onDiscoveredUsbDeviceListener == null) { return; } - UsbTransport usbTransport = UsbTransport.createUsbTransport(mActivity.getBaseContext(), usbDevice); - mListener.usbTransportDiscovered(usbTransport); + UsbTransport usbTransport = UsbTransport.createUsbTransport(context, usbDevice); + onDiscoveredUsbDeviceListener.usbTransportDiscovered(usbTransport); } public interface OnDiscoveredUsbDeviceListener { void usbTransportDiscovered(UsbTransport usbTransport); } + + public static void requestPermissionForUsbDevice(Context context, UsbDevice usbDevice) { + UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + if (usbManager == null) { + return; + } + + Intent answerBroadcastIntent = new Intent(ACTION_USB_PERMISSION); + answerBroadcastIntent.setPackage(BuildConfig.APPLICATION_ID); + PendingIntent answerPendingIntent = PendingIntent.getBroadcast(context, 0, answerBroadcastIntent, 0); + + Timber.d("Requesting permission for " + usbDevice.getDeviceName()); + usbManager.requestPermission(usbDevice, answerPendingIntent); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java index 917459a3b..610106288 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/usb/UsbTransport.java @@ -210,9 +210,14 @@ public class UsbTransport implements Transport { @Nullable @Override public TokenType getTokenTypeIfAvailable() { - switch (usbDevice.getVendorId()) { + return getTokenTypeFromUsbDeviceInfo(usbDevice.getVendorId(), usbDevice.getProductId(), usbConnection.getSerial()); + } + + @Nullable + public static TokenType getTokenTypeFromUsbDeviceInfo(int vendorId, int productId, String serialNo) { + switch (vendorId) { case VENDOR_YUBICO: { - switch (usbDevice.getProductId()) { + switch (productId) { case PRODUCT_YUBIKEY_NEO_OTP_CCID: case PRODUCT_YUBIKEY_NEO_CCID: case PRODUCT_YUBIKEY_NEO_U2F_CCID: @@ -227,11 +232,10 @@ public class UsbTransport implements Transport { break; } case VENDOR_NITROKEY: { - switch (usbDevice.getProductId()) { + switch (productId) { case PRODUCT_NITROKEY_PRO: return TokenType.NITROKEY_PRO; case PRODUCT_NITROKEY_START: - String serialNo = usbConnection.getSerial(); SecurityTokenInfo.Version gnukVersion = SecurityTokenInfo.parseGnukVersionString(serialNo); boolean versionGreaterEquals125 = gnukVersion != null && SecurityTokenInfo.Version.create("1.2.5").compareTo(gnukVersion) <= 0; @@ -242,7 +246,6 @@ public class UsbTransport implements Transport { break; } case VENDOR_FSIJ: { - String serialNo = usbConnection.getSerial(); SecurityTokenInfo.Version gnukVersion = SecurityTokenInfo.parseGnukVersionString(serialNo); boolean versionGreaterEquals125 = gnukVersion != null && SecurityTokenInfo.Version.create("1.2.5").compareTo(gnukVersion) <= 0; @@ -253,8 +256,7 @@ public class UsbTransport implements Transport { } } - Timber.d("Unknown USB token. Vendor ID: " + usbDevice.getVendorId() + ", Product ID: " + - usbDevice.getProductId()); + Timber.d("Unknown USB token. Vendor ID: %s, Product ID: %s", vendorId, productId); return null; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java index e24c96cac..917c93d75 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java @@ -17,43 +17,29 @@ package org.sufficientlysecure.keychain.ui; + import android.app.Activity; -import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; -import android.os.Bundle; -import timber.log.Timber; +import org.sufficientlysecure.keychain.securitytoken.UsbConnectionDispatcher; public class UsbEventReceiverActivity extends Activity { - public static final String ACTION_USB_PERMISSION = - "org.sufficientlysecure.keychain.ui.USB_PERMISSION"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } @Override protected void onResume() { super.onResume(); - final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); Intent intent = getIntent(); if (intent != null) { if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); - - Timber.d("Requesting permission for " + usbDevice.getDeviceName()); - usbManager.requestPermission(usbDevice, - PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0)); + UsbConnectionDispatcher.requestPermissionForUsbDevice(getApplicationContext(), usbDevice); } } - // Close the activity finish(); } } \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java index 7111aae0d..b0ac37cf7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java @@ -29,7 +29,6 @@ import android.nfc.TagLostException; import android.os.AsyncTask; import android.os.Bundle; -import nordpol.android.OnDiscoveredTagListener; import nordpol.android.TagDispatcher; import nordpol.android.TagDispatcherBuilder; import org.sufficientlysecure.keychain.R; @@ -56,8 +55,7 @@ import org.sufficientlysecure.keychain.util.Passphrase; import timber.log.Timber; -public abstract class BaseSecurityTokenActivity extends BaseActivity - implements OnDiscoveredTagListener, UsbConnectionDispatcher.OnDiscoveredUsbDeviceListener { +public abstract class BaseSecurityTokenActivity extends BaseActivity { public static final int REQUEST_CODE_PIN = 1; public static final String EXTRA_TAG_HANDLING_ENABLED = "tag_handling_enabled"; @@ -108,8 +106,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity onSecurityTokenError(error); } - @Override - public void tagDiscovered(Tag tag) { + private void nfcTagDiscovered(Tag tag) { // Actual NFC operations are executed in doInBackground to not block the UI thread if (!mTagHandlingEnabled) { return; @@ -119,8 +116,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity securityTokenDiscovered(nfcTransport); } - @Override - public void usbTransportDiscovered(UsbTransport usbTransport) { + private void usbTransportDiscovered(UsbTransport usbTransport) { // Actual USB operations are executed in doInBackground to not block the UI thread if (!mTagHandlingEnabled) { return; @@ -184,14 +180,14 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mNfcTagDispatcher = new TagDispatcherBuilder(this, this) + mNfcTagDispatcher = new TagDispatcherBuilder(this, this::nfcTagDiscovered) .enableUnavailableNfcUserPrompt(false) .enableSounds(true) .enableDispatchingOnUiThread(true) .enableBroadcomWorkaround(false) .build(); - mUsbDispatcher = new UsbConnectionDispatcher(this, this); + mUsbDispatcher = new UsbConnectionDispatcher(this, this::usbTransportDiscovered); // Check whether we're recreating a previously destroyed instance if (savedInstanceState != null) {