diff --git a/app/build.gradle b/app/build.gradle index a80f346..707a885 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,11 +23,15 @@ android { if (project.hasProperty('strictCheck')) { warningsAsErrors = strictCheck } + textReport true } } repositories { + maven { + url "file://${System.properties['user.home']}/.m2/repository" + } maven { url 'http://releases.marmeladburk.fidesmo.com' } @@ -45,6 +49,6 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:25.3.1' compile 'io.reactivex:rxjava:1.0.13' - compile 'com.fidesmo:ble-client-android:0.1.10' + compile 'com.fidesmo:ble-client-android:0.1.11' compile 'com.fidesmo:nordpol-android:0.1.18' } diff --git a/app/src/main/java/com/fidesmo/ble/client/BlePeripheralService.java b/app/src/main/java/com/fidesmo/ble/client/BlePeripheralService.java index 21b60fe..3d12e73 100644 --- a/app/src/main/java/com/fidesmo/ble/client/BlePeripheralService.java +++ b/app/src/main/java/com/fidesmo/ble/client/BlePeripheralService.java @@ -17,6 +17,7 @@ import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.widget.Toast; + import com.fidesmo.ble.client.protocol.FragmentationProtocol; import com.fidesmo.ble.client.protocol.PacketDefragmenter; import com.fidesmo.ble.client.protocol.PacketFragmenter; @@ -213,28 +214,17 @@ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, i return ; } + if (characteristic.getUuid().equals(BleCard.APDU_READ_CHARACTERISTIC_UUID) && currentResponsePacket != null) { - if (!characteristic.getUuid().equals(BleCard.APDU_READ_CHARACTERISTIC_UUID)) { - gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null); - log("Unsupported characteristics read: " + characteristic.getUuid()); - return ; - } - - if (currentResponsePacket == null) { - log("No answer ready yet"); - gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null); - return; - } - - if (currentResponsePacket.hasMoreData()) { - gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, currentResponsePacket.nextFragment()); - } else { - gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, new byte[]{0}); - } - + if (offset == 0 && currentResponsePacket.hasMoreData()) { + // save Fidesmo's internal packet inside characteristic + characteristic.setValue(currentResponsePacket.nextFragment()); + } - if (!currentResponsePacket.hasMoreData()) { - currentResponsePacket = null; + int chunkSize = characteristic.getValue().length - offset; + byte[] chunk = new byte[chunkSize]; + System.arraycopy(characteristic.getValue(), offset, chunk, 0, chunkSize); + gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, chunk); } } @@ -249,37 +239,20 @@ public void onCharacteristicWriteRequest(final BluetoothDevice device, final int ", flags: prepared=" + preparedWrite + ", respNeeded=" + responseNeeded + ", offset: " + offset ); - if (responseNeeded) { - BleUtils.retryCall(new Callable() { - @Override - public Boolean call() throws Exception { - return gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, new byte[]{0}); - } - }); - } - - if (offset != 0) { - log("Offset is not zero: " + offset); - return; - } - if(APDU_CONVERSATION_FINISHED_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) { finishConversation(); return; } - - if (currentPacketBuilder == null) { - log("Starting APDU request session"); - currentPacketBuilder = fragmentationProtocol.deframenter(); + if (preparedWrite) { + onWriteBuffer(value); + }else { + onWritePacket(value); } - currentPacketBuilder.append(value); - - if (currentPacketBuilder.complete()) { - log("Packet received"); - sendBleAPDUToActivity(currentPacketBuilder.getBuffer()); - currentPacketBuilder = null; + if (gattServer != null && responseNeeded) { + // need to give the same values we got as an reply, in order to get next possible part. + gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, value); } } @@ -300,8 +273,10 @@ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, Blue @Override public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { + super.onExecuteWrite(device, requestId, execute); log("onExecuteWrite(" + requestId + "), execute: " + execute); - gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null); + gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, new byte[] {}); + onExecuteWriteBuffer(execute); } @Override @@ -370,6 +345,38 @@ public void onMtuChanged(BluetoothDevice device, int mtu) { } } + private void onWriteBuffer(byte[] buffer) { + if (currentPacketBuilder == null) { + currentPacketBuilder = fragmentationProtocol.deframenter(); + currentPacketBuilder.appendPacket(buffer); + } else { + currentPacketBuilder.addPacketFragment(buffer); + } + } + + private void onWritePacket(byte[] buffer) { + if (currentPacketBuilder == null) { + currentPacketBuilder = fragmentationProtocol.deframenter(); + currentPacketBuilder.appendPacket(buffer); + onApduBufferReady(); + } + } + + private void onExecuteWriteBuffer(boolean execute) { + if (execute) { + onApduBufferReady(); + } else { + currentPacketBuilder = null; + } + } + + private void onApduBufferReady() { + if (currentPacketBuilder.isCompleted()) { + sendBleAPDUToActivity(currentPacketBuilder.fullData()); + currentPacketBuilder = null; + } + } + private void finishConversation() { Intent intent = new Intent(BlePeripheralService.CONVERSATION_FINISHED); LocalBroadcastManager.getInstance(BlePeripheralService.this).sendBroadcast(intent); diff --git a/app/src/main/java/com/fidesmo/ble/client/MainActivity.java b/app/src/main/java/com/fidesmo/ble/client/MainActivity.java index 4f7ce4c..be99bd7 100644 --- a/app/src/main/java/com/fidesmo/ble/client/MainActivity.java +++ b/app/src/main/java/com/fidesmo/ble/client/MainActivity.java @@ -23,6 +23,7 @@ import android.widget.Toast; import com.fidesmo.ble.R; import com.fidesmo.ble.client.apdu.CardInfoClient; +import com.fidesmo.ble.client.apdu.SimpleApduFragmenter; import com.fidesmo.ble.client.models.CardInfo; import com.fidesmo.ble.client.models.CardOperation; import nordpol.IsoCard; @@ -50,6 +51,8 @@ public class MainActivity extends AppCompatActivity implements OnDiscoveredTagLi private LinkedList pendingOperations = new LinkedList<>(); + private SimpleApduFragmenter apduFragmenter = new SimpleApduFragmenter(); + private BroadcastReceiver apduReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -101,7 +104,6 @@ public void onReceive(Context context, Intent intent) { nfcTagDispatcher = TagDispatcher.get(this, this, false, false, false, true, false, true); } - @Override public void tagDiscovered(Tag tag) { try { @@ -271,9 +273,12 @@ private void processPendingCardOperations() { } Log.i(TAG, "Trying to transcieve data to a card: " + byteArrayToString(operation.getRequest())); - - byte[] result = nfcCard.transceive(operation.getRequest()); - operation.setResponse(result); + byte[][] apdus = apduFragmenter.decode(operation.getRequest()); + byte[][] response = new byte[apdus.length][]; + for (int i = 0; i < apdus.length; i ++) { + response[i] = nfcCard.transceive(apdus[i]); + } + operation.setResponse(apduFragmenter.encode(response)); sendResponse(operation); operation = pendingOperations.poll(); diff --git a/app/src/main/java/com/fidesmo/ble/client/apdu/ApduFragmenter.java b/app/src/main/java/com/fidesmo/ble/client/apdu/ApduFragmenter.java new file mode 100644 index 0000000..67c6a2f --- /dev/null +++ b/app/src/main/java/com/fidesmo/ble/client/apdu/ApduFragmenter.java @@ -0,0 +1,8 @@ +package com.fidesmo.ble.client.apdu; + +public interface ApduFragmenter { + + byte[][] decode(byte[] encodedApdus); + byte[] encode(byte[][] apdus); + +} diff --git a/app/src/main/java/com/fidesmo/ble/client/apdu/SimpleApduFragmenter.java b/app/src/main/java/com/fidesmo/ble/client/apdu/SimpleApduFragmenter.java new file mode 100644 index 0000000..62a25d6 --- /dev/null +++ b/app/src/main/java/com/fidesmo/ble/client/apdu/SimpleApduFragmenter.java @@ -0,0 +1,64 @@ +package com.fidesmo.ble.client.apdu; + +import android.util.Log; + +import com.fidesmo.ble.client.BleUtils; +import com.fidesmo.ble.client.Utils; + +/** + * This class splits big byte buffer formed by a list of apdus + * into a list of single apdus. + * Spec: https://github.com/fidesmo/apdu-over-ble/blob/master/spec/apdu-service-spec.md + * + * Created by Angel Anton on 30/05/17. + */ + +public class SimpleApduFragmenter implements ApduFragmenter { + + private int TOTAL_HEADER_SIZE = 2; + private int HEADER_SIZE = 2; + + @Override + public byte[][] decode(byte[] encodedApdus) { + int totalNo = BleUtils.unpackInt2(encodedApdus, 0); + byte[][] apdus = new byte[totalNo][]; + int acc = TOTAL_HEADER_SIZE; + + for (int i = 0; i < totalNo; i++) { + int apduSize = BleUtils.unpackInt2(encodedApdus, acc); + byte[] apdu = new byte[apduSize]; + acc += HEADER_SIZE; + System.arraycopy(encodedApdus, acc, apdu, 0, apduSize); + apdus[i] = apdu; + acc += apduSize; + } + return apdus; + } + + @Override + public byte[] encode(byte[][] apdus) { + byte[] buffer = new byte[2]; + BleUtils.packInt2(apdus.length, buffer, 0); + int acc = TOTAL_HEADER_SIZE; + + for (int i = 0; i < apdus.length; i++) { + byte[] apdu = apdus[i]; + buffer = ensureBuffer(buffer, buffer.length + HEADER_SIZE + apdu.length); + BleUtils.packInt2(apdu.length, buffer, acc); + acc += HEADER_SIZE; + System.arraycopy(apdu, 0, buffer, acc, apdu.length); + acc += apdu.length; + } + + return buffer; + } + + private byte[] ensureBuffer(byte[] buffer, int size) { + if (buffer.length < size) { + byte[] oldBuf = buffer; + buffer = new byte[size]; // + 256? + System.arraycopy(oldBuf, 0, buffer, 0, oldBuf.length); + } + return buffer; + } +}