OTG: Add usb device manager prototype

This commit is contained in:
Nikita Mikhailov
2016-04-04 00:33:59 +06:00
parent 834440199a
commit bd2906a887
5 changed files with 219 additions and 28 deletions

View File

@@ -0,0 +1,7 @@
package org.sufficientlysecure.keychain.javacard;
import android.hardware.usb.UsbDevice;
public interface OnDiscoveredUsbDeviceListener {
void usbDeviceDiscovered(UsbDevice usbDevice);
}

View File

@@ -0,0 +1,135 @@
package org.sufficientlysecure.keychain.javacard;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
public class UsbConnectionManager {
private static final String LOG_TAG = UsbConnectionManager.class.getName();
private static final String ACTION_USB_PERMISSION = Constants.PACKAGE_NAME + ".USB_PERMITSSION";
private Activity mActivity;
private OnDiscoveredUsbDeviceListener mListener;
private final Semaphore mRunning = new Semaphore(1);
private final Set<UsbDevice> mProcessedDevices = Collections.newSetFromMap(new ConcurrentHashMap<UsbDevice, Boolean>());
/**
* Receives broadcast when a supported USB device is attached, detached or
* when a permission to communicate to the device has been granted.
*/
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
UsbDevice usbDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
String deviceName = usbDevice.getDeviceName();
if (ACTION_USB_PERMISSION.equals(action)) {
boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED,
false);
Log.d(LOG_TAG, "ACTION_USB_PERMISSION: " + permission + " Device: " + deviceName);
interceptIntent(intent);
context.unregisterReceiver(mUsbReceiver);
}
}
};
private final Thread mWatchThread = new Thread() {
@Override
public void run() {
final UsbManager usbManager = (UsbManager) mActivity.getSystemService(Context.USB_SERVICE);
while (true) {
mRunning.acquireUninterruptibly();
mRunning.release();
//
final UsbDevice device = getDevice(usbManager);
if (device != null && !mProcessedDevices.contains(device)) {
mProcessedDevices.add(device);
final Intent intent = new Intent(ACTION_USB_PERMISSION);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_USB_PERMISSION);
mActivity.registerReceiver(mUsbReceiver, filter);
usbManager.requestPermission(device, PendingIntent.getBroadcast(mActivity, 0, intent, 0));
}
try {
sleep(1000);
} catch (InterruptedException ignored) {
}
}
}
};
public UsbConnectionManager(final Activity activity, final OnDiscoveredUsbDeviceListener listener) {
this.mActivity = activity;
this.mListener = listener;
mRunning.acquireUninterruptibly();
mWatchThread.start();
}
public void startListeningForDevices() {
mRunning.release();
}
public void stopListeningForDevices() {
mRunning.acquireUninterruptibly();
}
public void interceptIntent(final Intent intent) {
if (intent == null || intent.getAction() == null) return;
switch (intent.getAction()) {
case UsbManager.ACTION_USB_DEVICE_ATTACHED: {
final UsbManager usbManager = (UsbManager) mActivity.getSystemService(Context.USB_SERVICE);
final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Intent usbI = new Intent(mActivity, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
usbI.setAction(ACTION_USB_PERMISSION);
usbI.putExtra(UsbManager.EXTRA_DEVICE, device);
PendingIntent pi = PendingIntent.getActivity(mActivity, 0, usbI, PendingIntent.FLAG_CANCEL_CURRENT);
usbManager.requestPermission(device, pi);
break;
}
case ACTION_USB_PERMISSION: {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null)
mListener.usbDeviceDiscovered(device);
break;
}
default:
break;
}
}
private static UsbDevice getDevice(UsbManager manager) {
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
for (UsbDevice device : deviceList.values()) {
Log.d(LOG_TAG, device.getDeviceName() + " " + device.getDeviceId());
if (device.getVendorId() == 0x1050 && device.getProductId() == 0x0112) {
Log.d(LOG_TAG, device.getDeviceName() + " OK");
return device;
}
}
return null;
}
}

View File

@@ -20,42 +20,32 @@
package org.sufficientlysecure.keychain.ui.base;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.interfaces.RSAPrivateCrtKey;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.os.AsyncTask;
import android.os.Bundle;
import nordpol.Apdu;
import nordpol.IsoCard;
import nordpol.android.TagDispatcher;
import nordpol.android.AndroidCard;
import nordpol.android.OnDiscoveredTagListener;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.javacard.BaseJavacardDevice;
import org.sufficientlysecure.keychain.javacard.JavacardDevice;
import org.sufficientlysecure.keychain.javacard.NfcTransport;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.javacard.OnDiscoveredUsbDeviceListener;
import org.sufficientlysecure.keychain.javacard.UsbConnectionManager;
import org.sufficientlysecure.keychain.javacard.UsbTransport;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService.KeyNotFoundException;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
@@ -66,27 +56,27 @@ import org.sufficientlysecure.keychain.ui.dialog.FidesmoPgpInstallDialog;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.Iso7816TLV;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implements OnDiscoveredTagListener {
import java.io.IOException;
import nordpol.IsoCard;
import nordpol.android.AndroidCard;
import nordpol.android.OnDiscoveredTagListener;
import nordpol.android.TagDispatcher;
public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
implements OnDiscoveredTagListener, OnDiscoveredUsbDeviceListener {
public static final int REQUEST_CODE_PIN = 1;
public static final String EXTRA_TAG_HANDLING_ENABLED = "tag_handling_enabled";
private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android";
//protected Passphrase mPin;
//protected Passphrase mAdminPin;
//protected boolean mPw1ValidForMultipleSignatures;
//protected boolean mPw1ValidatedForSignature;
//protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
//protected boolean mPw3Validated;
public JavacardDevice mJavacardDevice;
protected TagDispatcher mTagDispatcher;
// private IsoCard mIsoCard;
protected UsbConnectionManager mUsbDispatcher;
private boolean mTagHandlingEnabled;
private byte[] mNfcFingerprints;
@@ -185,6 +175,43 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
}.execute();
}
public void usbDeviceDiscovered(final UsbDevice device) {
// Actual NFC operations are executed in doInBackground to not block the UI thread
if(!mTagHandlingEnabled)
return;
new AsyncTask<Void, Void, IOException>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
onNfcPreExecute();
}
@Override
protected IOException doInBackground(Void... params) {
try {
handleUsbDevice(device);
} catch (IOException e) {
return e;
}
return null;
}
@Override
protected void onPostExecute(IOException exception) {
super.onPostExecute(exception);
if (exception != null) {
handleNfcError(exception);
return;
}
onNfcPostExecute();
}
}.execute();
}
protected void pauseTagHandling() {
mTagHandlingEnabled = false;
}
@@ -198,6 +225,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
super.onCreate(savedInstanceState);
mTagDispatcher = TagDispatcher.get(this, this, false, false, true, false);
mUsbDispatcher = new UsbConnectionManager(this, this);
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
@@ -228,7 +256,9 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
*/
@Override
public void onNewIntent(final Intent intent) {
mTagDispatcher.interceptIntent(intent);
if (!mTagDispatcher.interceptIntent(intent)) {
mUsbDispatcher.interceptIntent(intent);
}
}
private void handleNfcError(IOException e) {
@@ -346,6 +376,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
Log.d(Constants.TAG, "BaseNfcActivity.onPause");
mTagDispatcher.disableExclusiveNfc();
mUsbDispatcher.stopListeningForDevices();
}
/**
@@ -356,6 +387,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
super.onResume();
Log.d(Constants.TAG, "BaseNfcActivity.onResume");
mTagDispatcher.enableExclusiveNfc();
mUsbDispatcher.startListeningForDevices();
}
protected void obtainSecurityTokenPin(RequiredInputParcel requiredInput) {
@@ -372,7 +404,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT,
RequiredInputParcel.createRequiredPassphrase(requiredInput));
startActivityForResult(intent, REQUEST_CODE_PIN);
} catch (KeyNotFoundException e) {
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new AssertionError(
"tried to find passphrase for non-existing key. this is a programming error!");
}
@@ -425,6 +457,14 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen
doNfcInBackground();
}
protected void handleUsbDevice(UsbDevice device) throws IOException {
UsbManager usbManager = (UsbManager) getSystemService(USB_SERVICE);
mJavacardDevice = new BaseJavacardDevice(new UsbTransport(device, usbManager));
mJavacardDevice.connectToDevice();
doNfcInBackground();
}
public boolean isNfcConnected() {
return mJavacardDevice.isConnected();
}