OTG: Add usb device manager prototype
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package org.sufficientlysecure.keychain.javacard;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
|
||||
public interface OnDiscoveredUsbDeviceListener {
|
||||
void usbDeviceDiscovered(UsbDevice usbDevice);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user