From 5f880d3816526157c5d411896707b971af48212b Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Mon, 12 Jan 2015 22:47:57 +0100 Subject: o Major refactoring of the BT promise. --- app/app.iml | 117 ---------------- .../java/io/trygvis/android/bt/BtCallback.java | 8 ++ .../main/java/io/trygvis/android/bt/BtDevice.java | 150 ++++++++++++++++++--- .../main/java/io/trygvis/android/bt/BtPromise.java | 116 +++++++++------- .../io/trygvis/android/bt/DefaultBtService.java | 5 + .../android/bt/WrappingBluetoothGattCallback.java | 60 --------- .../soilmoisture/DefaultSoilMoistureService.java | 37 +---- 7 files changed, 214 insertions(+), 279 deletions(-) delete mode 100644 app/app.iml delete mode 100644 app/src/main/java/io/trygvis/android/bt/WrappingBluetoothGattCallback.java diff --git a/app/app.iml b/app/app.iml deleted file mode 100644 index 7d4711d..0000000 --- a/app/app.iml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/java/io/trygvis/android/bt/BtCallback.java b/app/src/main/java/io/trygvis/android/bt/BtCallback.java index 71f7d56..7ff8733 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtCallback.java +++ b/app/src/main/java/io/trygvis/android/bt/BtCallback.java @@ -62,4 +62,12 @@ public class BtCallback { public void onFinally() { throw new NotOverriddenException(); } + + @Override + public String toString() { + return "BtCallback{" + + "name='" + name + '\'' + + ", stopOnFailure=" + stopOnFailure + + '}'; + } } diff --git a/app/src/main/java/io/trygvis/android/bt/BtDevice.java b/app/src/main/java/io/trygvis/android/bt/BtDevice.java index d8dfefc..2a4a5d8 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtDevice.java +++ b/app/src/main/java/io/trygvis/android/bt/BtDevice.java @@ -3,11 +3,16 @@ package io.trygvis.android.bt; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import java.util.Date; +import static io.trygvis.android.bt.BtPromise.PromiseResult.detour; +import static io.trygvis.android.bt.BtPromise.PromiseResult.stop; + public class BtDevice> { private final static String TAG = BtDevice.class.getSimpleName(); @@ -23,6 +28,7 @@ public class BtDevice> { private final Date firstSeen; private Date lastSeen; private boolean connected; + private final WrappingBluetoothGattCallback wrappingCallback = new WrappingBluetoothGattCallback(); public static interface BtDeviceWrapper> { BtDevice getBtDevice(); @@ -83,27 +89,69 @@ public class BtDevice> { this.lastSeen = lastSeen; } - public synchronized boolean connect(BtPromise executor) { - Log.i(TAG, "connect(), address=" + bluetoothDevice.getAddress()); - BluetoothGattCallback callback = executor.asCallback(bluetoothDevice.getAddress()); - gatt = bluetoothDevice.connectGatt(btService, false, new WrappingBluetoothGattCallback(callback) { - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - BtDevice.this.connected = newState == BluetoothGatt.STATE_CONNECTED; - - super.onConnectionStateChange(gatt, status, newState); - } - }); - return true; - } +// public synchronized boolean connect(BtPromise executor) { +// Log.i(TAG, "connect(), address=" + bluetoothDevice.getAddress()); +// BluetoothGattCallback callback = executor.asCallback(bluetoothDevice.getAddress()); +// gatt = bluetoothDevice.connectGatt(btService, false, new WrappingBluetoothGattCallback(callback) { +// @Override +// public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { +// BtDevice.this.connected = newState == BluetoothGatt.STATE_CONNECTED; +// +// super.onConnectionStateChange(gatt, status, newState); +// } +// }); +// return true; +// } + + private BluetoothGattCallback callback; + + public synchronized void withConnection(BtPromise promise) { + if (callback != null) { + throw new RuntimeException("The current callback is not done."); + } - public synchronized void disconnect() { - if (gatt != null) { - gatt.disconnect(); - gatt = null; + Log.i(TAG, "withConnection(), address=" + bluetoothDevice.getAddress() + ", connected: " + (gatt != null)); + + if (gatt == null) { + callback = new BtPromise(). + ignoreFailureForNext(). + onConnectionStateChange((gatt, status, newState) -> { + Log.i(TAG, "defaultConnectCallback: status=" + status + ", newState=" + newState); + String address = gatt.getDevice().getAddress(); + //noinspection SimplifiableIfStatement + if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) { + Log.i(TAG, "Connected to " + address); + return detour(promise); + } else { + Log.i(TAG, "Could not connect to " + address + ", trying again"); + + return detour(new BtPromise().onConnectionStateChange((gatt2, status2, newState2) -> { + if (status2 == BluetoothGatt.GATT_SUCCESS && newState2 == BluetoothGatt.STATE_CONNECTED) { + Log.i(TAG, "Connected to " + address); + return detour(promise); + } + + Log.i(TAG, "Could still not connect to " + address + ", failing."); + + return stop(); + })); + } + }). + asCallback(bluetoothDevice.getAddress()); + } else { + callback = promise.asCallback(bluetoothDevice.getAddress()); } + + gatt = bluetoothDevice.connectGatt(btService, false, wrappingCallback); } +// public synchronized void disconnect() { +// if (gatt != null) { +// gatt.disconnect(); +// gatt = null; +// } +// } + public boolean connected() { return connected; } @@ -131,4 +179,72 @@ public class BtDevice> { public int hashCode() { return getAddress().hashCode(); } + + private class WrappingBluetoothGattCallback extends BluetoothGattCallback { + + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + BtDevice.this.connected = newState == BluetoothGatt.STATE_CONNECTED; + + if (callback != null) { + callback.onConnectionStateChange(gatt, status, newState); + } + } + + @Override + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + if (callback != null) { + callback.onServicesDiscovered(gatt, status); + } + } + + @Override + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + if (callback != null) { + callback.onCharacteristicRead(gatt, characteristic, status); + } + } + + @Override + public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + if (callback != null) { + callback.onCharacteristicWrite(gatt, characteristic, status); + } + } + + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + if (callback != null) { + callback.onCharacteristicChanged(gatt, characteristic); + } + } + + @Override + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + if (callback != null) { + callback.onDescriptorRead(gatt, descriptor, status); + } + } + + @Override + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + if (callback != null) { + callback.onDescriptorWrite(gatt, descriptor, status); + } + } + + @Override + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { + if (callback != null) { + callback.onReliableWriteCompleted(gatt, status); + } + } + + @Override + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + if (callback != null) { + callback.onReadRemoteRssi(gatt, rssi, status); + } + } + } } diff --git a/app/src/main/java/io/trygvis/android/bt/BtPromise.java b/app/src/main/java/io/trygvis/android/bt/BtPromise.java index 6af62c0..d4b3b67 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtPromise.java +++ b/app/src/main/java/io/trygvis/android/bt/BtPromise.java @@ -6,12 +6,9 @@ import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.util.Log; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; -import java.util.Queue; import io.trygvis.android.F2; import io.trygvis.android.F3; @@ -32,8 +29,8 @@ import static io.trygvis.android.bt.BtPromise.EventType.onServicesDiscovered; public class BtPromise { private final static String TAG = BtPromise.class.getSimpleName(); private final List actionQ = new ArrayList<>(); - private final Queue failureQ = new ArrayDeque<>(); - private final Queue finallyQ = new ArrayDeque<>(); + private final List failureQ = new ArrayList<>(); + private final List finallyQ = new ArrayList<>(); private static final PromiseResult waitForNextEvent = new WaitForNextEvent(); private static final PromiseResult stop = new Stop(); @@ -84,7 +81,7 @@ public class BtPromise { } private boolean stopOnFailure() { - if(stopOnFailure != null) { + if (stopOnFailure != null) { boolean b = stopOnFailure; stopOnFailure = null; return b; @@ -96,12 +93,12 @@ public class BtPromise { stopOnFailure = true; return this; } - + public synchronized BtPromise onConnectionStateChange(F3 callback) { actionQ.add(new BtCallback(stopOnFailure(), "onConnectionStateChange") { @Override public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - return callback.apply(gatt, status,newState); + return callback.apply(gatt, status, newState); } }); return this; @@ -296,17 +293,17 @@ public class BtPromise { private final String address; private List actionQ; - private ListIterator it; - private ArrayDeque failureQ; - ArrayDeque finallyQ; + private int currentAction = 0; + private List failureQ; + private List finallyQ; private List events = new ArrayList<>(); - private BtBluetoothGattCallback(String address, List actionQ, Queue failureQ, Queue finallyQ) { + private BtBluetoothGattCallback(String address, List actionQ, + List failureQ, List finallyQ) { this.address = address; this.actionQ = new ArrayList<>(actionQ); - this.failureQ = new ArrayDeque<>(failureQ); - this.finallyQ = new ArrayDeque<>(finallyQ); - this.it = actionQ.listIterator(); + this.failureQ = new ArrayList<>(failureQ); + this.finallyQ = new ArrayList<>(finallyQ); } void onEvent(EventType key, String values, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, @@ -316,22 +313,28 @@ public class BtPromise { Log.i(TAG, "event: " + key + "(" + values + "), success=" + success); - if (!success) { - doFailure(); - return; - } - BtCallback btCallback; synchronized (this) { - if (!it.hasNext()) { - Log.d(TAG, "All Bluetooth actions are done"); + if (!hasNext()) { + Log.d(TAG, "All Bluetooth actions are done, no handler for last event."); + showEvents(); doFinally(); return; } - btCallback = it.next(); - Log.i(TAG, "Executing bt action: " + btCallback.name); + btCallback = actionQ.get(currentAction++); + + if (!success) { + if (btCallback.stopOnFailure) { + doFailure(); + return; + } else { + Log.i(TAG, "Last status was a failure, but the callback still want it."); + } + } + + Log.i(TAG, "Graceful sleep" + btCallback.name); try { Thread.sleep(1000); } catch (InterruptedException e) { @@ -340,6 +343,7 @@ public class BtPromise { } try { + Log.i(TAG, "Executing bt action: " + btCallback.name); PromiseResult result = callCallback(key, gatt, characteristic, descriptor, status, newState, btCallback, value); @@ -347,20 +351,23 @@ public class BtPromise { Log.i(TAG, "The chain want to stop."); doFinally(); } else if (result instanceof Detour) { - Log.i(TAG, "Adding detour"); - BtPromise promise = ((Detour) result).promise; - events.add("detour " + promise.actionQ.size()); - if (!promise.failureQ.isEmpty()) { - Log.i(TAG, "Ignoring " + promise.failureQ.size() + " items from the failure Q"); - } - if (!promise.finallyQ.isEmpty()) { - Log.i(TAG, "Ignoring " + promise.finallyQ.size() + " items from the finally Q"); + BtPromise detour = ((Detour) result).promise; + Log.i(TAG, "Adding detour with " + detour.actionQ.size() + " actions."); + events.add("detour, action size=" + detour.actionQ.size() + ", failure size=" + + detour.failureQ.size() + ", finally size=" + detour.finallyQ.size()); + + Log.i(TAG, "hasNext(): " + hasNext()); + Log.i(TAG, "currentAction: " + currentAction); + if (hasNext()) { + Log.i(TAG, "next action: " + actionQ.get(currentAction).name); } - synchronized (this) { - for (BtCallback cb : promise.actionQ) { - it.add(cb); - } + actionQ.addAll(currentAction, detour.actionQ); + failureQ.addAll(detour.failureQ); + finallyQ.addAll(detour.finallyQ); + Log.i(TAG, "hasNext(): " + hasNext()); + if (hasNext()) { + Log.i(TAG, "next action: " + actionQ.get(currentAction).name); } } else if (result instanceof ContinueDirectly) { value = ((ContinueDirectly) result).value; @@ -369,8 +376,9 @@ public class BtPromise { return; } - if (!it.hasNext()) { + if (!hasNext()) { Log.i(TAG, "The queue is empty"); + showEvents(); } } catch (NotOverriddenException e) { Log.w(TAG, "Unexpected callback by listener: " + key); @@ -378,33 +386,37 @@ public class BtPromise { } } + private boolean hasNext() { + return currentAction < actionQ.size(); + } + private void doFailure() { + showEvents(); + + Log.w(TAG, "Executing " + failureQ.size() + " failure handlers"); + + for (BtCallback callback : failureQ) { + callCallback(onFailure, null, null, null, 0, 0, callback, null); + } + + doFinally(); + } + + private void showEvents() { StringBuilder msg = new StringBuilder(); msg.append("Address: ").append(address).append("\n"); - msg.append("Expected events: \n"); + msg.append("Event handlers: \n"); for (BtCallback cb : actionQ) { msg.append("- ").append(cb.name).append("\n"); } - msg.append("Actual events: \n"); + msg.append("Events received: \n"); for (String event : events) { msg.append("- ").append(event).append("\n"); } Log.w(TAG, msg.toString()); - - Log.w(TAG, "Executing " + failureQ.size() + " failure handlers"); - - for (BtCallback callback = failureQ.poll(); callback != null; callback = failureQ.poll()) { - try { - callCallback(onFailure, null, null, null, 0, 0, callback, null); - } catch (NotOverriddenException e) { - return; - } - } - - doFinally(); } private void doFinally() { @@ -412,7 +424,7 @@ public class BtPromise { Log.w(TAG, "Executing " + finallyQ.size() + " finally handlers"); - for (BtCallback callback = finallyQ.poll(); callback != null; callback = finallyQ.poll()) { + for (BtCallback callback : finallyQ) { try { callCallback(onFinally, null, null, null, 0, 0, callback, null); } catch (NotOverriddenException e) { diff --git a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java index cc7d484..718a1cd 100644 --- a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java +++ b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java @@ -185,6 +185,11 @@ public class DefaultBtService> extends Ser private BluetoothAdapter.LeScanCallback leScanCallback = (device, rssi, scanRecord) -> { BtScanResult scanResult = new BtScanResult(scanRecord); + if (!device.getAddress().startsWith("FB:")) { + Log.w(TAG, "filtering out device: " + device.getAddress()); + return; + } + register(device, rssi, scanResult); }; diff --git a/app/src/main/java/io/trygvis/android/bt/WrappingBluetoothGattCallback.java b/app/src/main/java/io/trygvis/android/bt/WrappingBluetoothGattCallback.java deleted file mode 100644 index 8d790d1..0000000 --- a/app/src/main/java/io/trygvis/android/bt/WrappingBluetoothGattCallback.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.trygvis.android.bt; - -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCallback; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; - -class WrappingBluetoothGattCallback extends BluetoothGattCallback { - - private final BluetoothGattCallback wrapped; - - public WrappingBluetoothGattCallback(BluetoothGattCallback wrapped) { - this.wrapped = wrapped; - } - - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - wrapped.onConnectionStateChange(gatt, status, newState); - } - - @Override - public void onServicesDiscovered(BluetoothGatt gatt, int status) { - wrapped.onServicesDiscovered(gatt, status); - } - - @Override - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - wrapped.onCharacteristicRead(gatt, characteristic, status); - } - - @Override - public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - wrapped.onCharacteristicWrite(gatt, characteristic, status); - } - - @Override - public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - wrapped.onCharacteristicChanged(gatt, characteristic); - } - - @Override - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { - wrapped.onDescriptorRead(gatt, descriptor, status); - } - - @Override - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { - wrapped.onDescriptorWrite(gatt, descriptor, status); - } - - @Override - public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { - wrapped.onReliableWriteCompleted(gatt, status); - } - - @Override - public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { - wrapped.onReadRemoteRssi(gatt, rssi, status); - } -} diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java index 3ff6e66..174c245 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java +++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java @@ -30,8 +30,6 @@ import io.trygvis.android.bt.BtService; import io.trygvis.android.bt.DefaultBtService; import io.trygvis.bluetooth.TrygvisIoUuids; -import static io.trygvis.android.bt.BtPromise.PromiseResult.continueDirectly; -import static io.trygvis.android.bt.BtPromise.PromiseResult.detour; import static io.trygvis.android.bt.BtPromise.PromiseResult.stop; import static io.trygvis.android.bt.BtPromise.PromiseResult.waitForNextEvent; import static io.trygvis.android.bt.BtService.BtServiceListenerBroadcastReceiver; @@ -117,8 +115,6 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS if (!smDevice.isProbed()) { Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName()); BtPromise executor = new BtPromise(). - ignoreFailureForNext(). - onConnectionStateChange(DefaultSoilMoistureService::defaultConnectCallback). onDirect(v -> { BluetoothGatt gatt = (BluetoothGatt) v; return gatt.discoverServices() ? waitForNextEvent() : stop(); @@ -162,15 +158,14 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS return stop(); }). onFinally(() -> { - btDevice.disconnect(); +// btDevice.disconnect(); if (smDevice.getIsUseful() == null) { smDevice.setIsUseful(false); } - }); - btDevice.connect(executor); + btDevice.withConnection(executor); } } }; @@ -208,29 +203,6 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS }); } - public static BtPromise.PromiseResult defaultConnectCallback(BluetoothGatt gatt, int status, Integer newState) { - Log.i(TAG, "defaultConnectCallback: status=" + status + ", newState=" + newState); - String address = gatt.getDevice().getAddress(); - //noinspection SimplifiableIfStatement - if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) { - Log.i(TAG, "Connected to " + address); - return continueDirectly(gatt); - } else { - Log.i(TAG, "Disconnected from " + address + ", trying again"); - - return detour(new BtPromise().onConnectionStateChange((gatt2, status2, newState2) -> { - if (newState2 == BluetoothGatt.STATE_CONNECTED) { - Log.i(TAG, "Connected to " + address); - return continueDirectly(gatt); - } - - Log.i(TAG, "Could still not connect to " + address + ", failing."); - - return stop(); - })); - } - } - // ----------------------------------------------------------------------- // SmDevicesManager Implementation // ----------------------------------------------------------------------- @@ -300,7 +272,6 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS void readCurrentValue(SmSensor sensor) { BtPromise promise = new BtPromise(). - onConnectionStateChange(DefaultSoilMoistureService::defaultConnectCallback). onDirect(v -> { BluetoothGatt gatt = (BluetoothGatt) v; return gatt.discoverServices() ? waitForNextEvent() : stop(); @@ -327,14 +298,14 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS handleNewSensorValueReady(sensor, getSensorCountRes.value); - gatt.disconnect(); +// gatt.disconnect(); return stop(); }). onFinally(() -> { }); - sensor.getDevice().getBtDevice().connect(promise); + sensor.getDevice().getBtDevice().withConnection(promise); } // ----------------------------------------------------------------------- -- cgit v1.2.3