Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLE communication refactoring #7

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
6 changes: 5 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand All @@ -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'
}
97 changes: 52 additions & 45 deletions app/src/main/java/com/fidesmo/ble/client/BlePeripheralService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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<Boolean>() {
@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);
}
}

Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 9 additions & 4 deletions app/src/main/java/com/fidesmo/ble/client/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -50,6 +51,8 @@ public class MainActivity extends AppCompatActivity implements OnDiscoveredTagLi

private LinkedList<CardOperation> pendingOperations = new LinkedList<>();

private SimpleApduFragmenter apduFragmenter = new SimpleApduFragmenter();

private BroadcastReceiver apduReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.fidesmo.ble.client.apdu;

public interface ApduFragmenter {

byte[][] decode(byte[] encodedApdus);
byte[] encode(byte[][] apdus);

}
Original file line number Diff line number Diff line change
@@ -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;
}
}