Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,15 @@ dependencies {
implementation 'com.github.d-max:spots-dialog:0.7@aar'

// Coppernic
implementation "fr.coppernic.sdk.core:CpcCore:3.8.0"
implementation("fr.coppernic.sdk.core:CpcCore:3.8.0") {
exclude group: "fr.coppernic.external", module: "D2xx"
}

implementation 'fr.coppernic.lib.splash:splash:0.4.1'
pcscv1Implementation 'fr.coppernic.sdk.pcsc:CpcPcsc:1.6.0'
pcscv2Implementation "fr.coppernic.sdk.pcsc2:CpcCore:3.7.0"
pcscv1Implementation 'fr.coppernic.sdk.pcsc:CpcPcsc:1.7.0'
pcscv2Implementation("fr.coppernic.sdk.pcsc2:CpcCore:3.7.0") {
exclude group: "fr.coppernic.external", module: "D2xx"
}

// RX-java
implementation 'io.reactivex.rxjava2:rxjava:2.2.10'
Expand All @@ -117,4 +122,9 @@ dependencies {
// Coroutine
implementation "androidx.lifecycle:lifecycle-common-java8:2.5.1"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"


// FTDI for Fusion support
pcscv1Implementation(files("libs/d2xx.jar"))

}
Binary file added app/libs/d2xx.jar
Binary file not shown.
22 changes: 21 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,26 @@
android:name="activity"
android:value="fr.coppernic.samples.pcsc.ui.MainActivity"/>
</activity>

<activity
android:name="fr.coppernic.sdk.usboutlet.UsbAttachActivity"
android:exported="true">

<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
</intent-filter>

<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter"/>
</activity>

<activity
android:name="fr.coppernic.sdk.usboutlet.UsbConfigurationActivity"
android:exported="false"
android:noHistory="true"
android:excludeFromRecents="true" />

</application>

</manifest>
</manifest>
13 changes: 13 additions & 0 deletions app/src/main/res/xml/device_filter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- res/xml/device_filter.xml -->
<resources>
<!--FTDI : VID = 0x0403 in decimal VID = 1027, PID = 0x6015 in decimal PID = 24597-->
<usb-device vendor-id="1027" product-id="24597" />
<!--Five-O : VID = 0x113F in decimal VID = 4415, PID = 0x1500 in decimal PID = 5376-->
<usb-device vendor-id="4415" product-id="5376" />
<!--OCR : VID = 0x0DB5 in decimal VID = 3509, PID = 0x013D in decimal PID = 317-->
<usb-device vendor-id="3509" product-id="317" />
<!--Iris : VID = 0x1F63 in decimal VID = 8035, PID = 0xC801 in decimal PID = 51201-->
<usb-device vendor-id="8035" product-id="51201" />
<!--Smart card SEC1210 : VID = 0x0424 in decimal VID = , PID = 0x1202 in decimal PID = -->
<usb-device vendor-id="1060" product-id="4610" />
</resources>
104 changes: 52 additions & 52 deletions app/src/pcscv1/java/fr/coppernic/samples/pcsc/ui/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package fr.coppernic.samples.pcsc.ui;

import static fr.coppernic.sdk.power.OutletPowerManager.ACTION_USB_PERMISSION;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
Expand All @@ -17,7 +13,10 @@
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.appcompat.widget.Toolbar;
Expand All @@ -33,6 +32,7 @@
import fr.coppernic.samples.pcsc.reader.PcscReader;
import fr.coppernic.sdk.pcsc.ApduResponse;
import fr.coppernic.sdk.power.OutletPowerManager;
import fr.coppernic.sdk.usboutlet.UsbConfigurationManager;
import fr.coppernic.sdk.utils.core.CpcBytes;
import fr.coppernic.sdk.utils.core.CpcResult;
import fr.coppernic.sdk.utils.core.CpcResult.RESULT;
Expand All @@ -41,6 +41,21 @@

public class MainActivity extends AppCompatActivity {

private UsbConfigurationManager usbConfigurationManager;

private final ActivityResultLauncher<Intent> usbConfigurationLauncher =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK) {
startPcscReader();
} else {
handleUsbConfigurationFailure();
}
}
);


FloatingActionButton fab;
Toolbar toolbar;
MaterialBetterSpinner spReader;
Expand Down Expand Up @@ -69,6 +84,25 @@ public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initUi();

usbConfigurationManager = new UsbConfigurationManager(this);
}

private void initializeUsb() {
// If SmartCard reader is powered and permission granted, no need to launch configuration activity
if (usbConfigurationManager.isSmartCardReaderReady()) {
startPcscReader();
} else {
usbConfigurationLauncher.launch(
UsbConfigurationManager.Companion.createConfigurationIntent(this)
);
}
}

private void initUi() {

initTitle();

fab = findViewById(R.id.fab);
Expand Down Expand Up @@ -113,37 +147,33 @@ public void afterTextChanged(Editable s) {

//Init empty spinner
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this,
android.R.layout.simple_dropdown_item_1line,
new ArrayList<String>());
android.R.layout.simple_dropdown_item_1line,
new ArrayList<String>());
spReader.setAdapter(arrayAdapter);
etResult.clearFocus();

etApdu.setOnEditorActionListener(editorActionListener);

showFAB(false);

swConnect.setEnabled(false);
}

private void handleUsbConfigurationFailure() {
Toast.makeText(this, "USB configuration failed", Toast.LENGTH_SHORT).show();
finish();
}

@Override
protected void onStart() {
Timber.d("onStart");
super.onStart();
showFAB(false);

// Register USB receiver
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_USB_PERMISSION);
registerReceiver(usbReceiver, filter, RECEIVER_EXPORTED);

powerOn();
initializeUsb();
}

@Override
protected void onStop() {
Timber.d("onStop");
// Unregister USB receiver
try {
unregisterReceiver(usbReceiver);
} catch (Exception e) {
e.printStackTrace();
}
stopPcscReader();
super.onStop();
}
Expand Down Expand Up @@ -199,38 +229,6 @@ private void stopPcscReader() {
}
//endregion


//region USB
private BroadcastReceiver usbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equalsIgnoreCase(action)) {
swConnect.setEnabled(true);
startPcscReader();
}
}
};
//endregion


// region Methods
void powerOn() {
if (!manager.isPowerOn(getApplicationContext())) {
Coroutines coroutines = new Coroutines();
manager.powerOn(MainActivity.this, 10000L, coroutines.getContinuation(
(res, err) -> {
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
manager.getUsbPermissions(MainActivity.this);
}
));
}
}

void connectCard() {
if (reader != null) {
if (!reader.isConnected()) {//Connect reader
Expand Down Expand Up @@ -325,6 +323,8 @@ private void updateSpinner() {
deviceList);
spReader.setAdapter(arrayAdapter);
}

swConnect.setEnabled(deviceList != null);
}
//endregion
}
83 changes: 83 additions & 0 deletions app/src/pcscv1/java/fr/coppernic/sdk/usboutlet/FTdiManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package fr.coppernic.sdk.usboutlet

import android.content.Context
import android.content.Context.USB_SERVICE
import android.hardware.usb.UsbManager
import android.util.Log
import com.ftdi.j2xx.D2xxManager
import com.ftdi.j2xx.FT_Device

const val SET_GPIO_OUTPUT_ALL_HIGH = 0xF0

const val SET_GPIO_OUTPUT_ALL_LOG = 0xFF

const val SET_BIT_MODE = 0x20
const val FTDI_CONFIGURATION_DELAY = 2000L


object FtdiManager {

private var ftDevice: FT_Device? = null
private const val TAG = "FtdiManager"

fun powerSmartCardReader(context: Context): Boolean {
val usbManager = context.applicationContext.getSystemService(USB_SERVICE) as UsbManager

val ftdiUsbDevice =
findUsbDevice(usbManager, FTDI_VID, listOf(FTDI_PID_1, FTDI_PID_2)) ?: return false


try {
val d2xxManager = D2xxManager.getInstance(context)
val count = d2xxManager.createDeviceInfoList(context)

if (count == 0) {
Log.e(TAG, "D2xx createDeviceInfoList() count == 0")
return false
}

ftDevice = d2xxManager.openByUsbDevice(
context, ftdiUsbDevice
)

if (ftDevice == null) {
Log.e(TAG, "D2xx openByUsbDevice() failed")
return false
}

ftDevice?.apply {
resetDevice()
setBaudRate(9600)
setDataCharacteristics(
D2xxManager.FT_DATA_BITS_8,
D2xxManager.FT_STOP_BITS_1,
D2xxManager.FT_PARITY_NONE
)
}

val mask = computeFtdiGpioOutputMask(false, false, false, true)
ftDevice?.setBitMode(mask, SET_BIT_MODE.toByte())

return true
} catch (e: Exception) {
Log.e(TAG, "FTDI communication failed", e)
return false
}
}


private fun computeFtdiGpioOutputMask(
gpio0: Boolean, gpio1: Boolean, gpio2: Boolean, gpio3: Boolean
): Byte {

val direction = 0xF shl 4
var values = 0
if (!gpio0) values = values or (1 shl 0)
if (!gpio1) values = values or (1 shl 1)
if (!gpio2) values = values or (1 shl 2)
if (!gpio3) values = values or (1 shl 3)

return (direction or values).toByte()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package fr.coppernic.sdk.usboutlet

import android.app.Activity
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import android.os.Bundle
import android.util.Log

class UsbAttachActivity : Activity() {
private val TAG = "UsbAttachActivity"

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val device =
intent.getParcelableExtra<UsbDevice?>(UsbManager.EXTRA_DEVICE)

if (device != null) {
val manager =
getSystemService(USB_SERVICE) as UsbManager

Log.d(
TAG,
"USB attached: " + device.deviceName +
", permission=" + manager.hasPermission(device)
)
}

finish()
}
}
Loading