1

Im performing a write to a BLE characteristic, where with these specific devices certain characteristics are not writeable if the peripheral is still locked.

I'd like to simply provide a visual notification if this is called while the device is still locked, instead of the exception being thrown.

Here is the first part of the code which is from a custom library I've written for this application:

public Observable<byte[]> setInterval(int interval) {
        if (!(interval > 0)) {
            return Observable.error(new IllegalArgumentException("Interval must be greater than 0."));
        }
        byte[] bytes = Utils.formatIntToBytes(interval);
        return mConnection.writeCharacteristic(Constants.INTERVAL, bytes)
                .doOnError(throwable -> Log.d(TAG, "call: Error writing to the interval characteristic"))
                .onErrorReturn(throwable -> {
                    return new byte[0];
                });
    }

And here is the method that uses the previous:

private void setInterval(int interval) {
       mDevice.setInterval(interval)
               .onErrorResumeNext(throwable -> {
                   return Observable.empty();
               })
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe(new Subscriber<byte[]>() {
                   @Override
                   public void onCompleted() {

                   }

                   @Override
                   public void onError(Throwable e) {
                       e.printStackTrace();
                   }

                   @Override
                   public void onNext(byte[] bytes) {
                       if (bytes != null) {
                           Toast.makeText(DeviceDetailActivity.this, Utils.formatResponse(bytes), Toast.LENGTH_SHORT).show();
                       } else {
                           Toast.makeText(DeviceDetailActivity.this, "Seems to of been an error. Ensure the device is unlocked.", Toast.LENGTH_SHORT).show();
                       }
                   }
               });
    }

This was previously just an Action1, but I changed to a Subscriber to clearly show onError() has been implemented.

I've added a bunch of extra operators (onErrorReturn(), onErrorResumeNext()) in an attempt to prevent the exception.

Why is the exception still coming through?

EDIT: The stack trace:

  java.lang.IllegalStateException: Exception thrown on Scheduler.Worker thread. Add `onError` handling.
      at rx.android.schedulers.LooperScheduler$ScheduledAction.run(LooperScheduler.java:112)
      at android.os.Handler.handleCallback(Handler.java:739)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:148)
      at android.app.ActivityThread.main(ActivityThread.java:5417)
      at java.lang.reflect.Method.invoke(Native Method)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
   Caused by: rx.exceptions.OnErrorNotImplementedException
      at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:386)
      at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:383)
      at rx.internal.util.ActionSubscriber.onError(ActionSubscriber.java:44)
      at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:152)
      at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:115)
      at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.checkTerminated(OperatorObserveOn.java:276)
      at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:219)
      at rx.android.schedulers.LooperScheduler$ScheduledAction.run(LooperScheduler.java:107)
      at android.os.Handler.handleCallback(Handler.java:739) 
      at android.os.Handler.dispatchMessage(Handler.java:95) 
      at android.os.Looper.loop(Looper.java:148) 
      at android.app.ActivityThread.main(ActivityThread.java:5417) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
   Caused by: BleGattException{status=8, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_WRITE'}}
      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicWrite(RxBleGattCallback.java:110)
      at android.bluetooth.BluetoothGatt$1.onCharacteristicWrite(BluetoothGatt.java:407)
      at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:279)
      at android.os.Binder.execTransact(Binder.java:453)
Orbit
  • 2,985
  • 9
  • 49
  • 106
  • Are you sure that it comes from this place in the code? `OnErrorNotImplementedException` should have a cause - maybe it will be useful to track down the actual part of the code. – Dariusz Seweryn Nov 30 '16 at 08:43
  • An activity receives an event via an Event Bus and simply calls the method in the second code example, which in turn calls the code in the first example. The event is triggered via a button click. I am quite sure no other code is relevant. I've added the stack trace to the question. – Orbit Nov 30 '16 at 18:50
  • Maybe something else at different place is also using `mConnection`? In `RxAndroidBle` any error that happens during the connection is propagated around to every subscriber that is listening to this `RxBleConnection`. So it is still possible that the error is not handled in a different place in the code. – Dariusz Seweryn Nov 30 '16 at 19:44
  • Could you give an update? It would be really good to know if there is an error in the library. – Dariusz Seweryn Dec 05 '16 at 16:01
  • @s_noopy I have yet to fix it. The stack trace does not mention any portion of my code, but does mention RxAndroidble, so perhaps there is an issue. – Orbit Dec 06 '16 at 23:05
  • I noticed something interesting: When I first connect to a device, I call a `setupNotifications()` method which uses `RxBleConnection.setupNotification()` to setup notifications for button clicks. When the original error is thrown when calling `setInterval`, the doOnError implemented in `setupNotifications` is called, even though it was already setup and working previously. – Orbit Dec 06 '16 at 23:22
  • That's true. Because of a stateful nature of BLE connection any error that is happening while `RxBleConnection` is working is propagated to every other functional `Observable` returned by the `RxBleConnection` that has not yet completed. Setup `.setupNotifications()` is never ending until unsubscribed so it will emit errors that will happen later on the connection. – Dariusz Seweryn Dec 07 '16 at 09:37
  • Ok, so when an error occurs any observables still emitting items will error out, right? Is there a better way to handle these scenarios? `mConnection` is obviously needed to perform any operations on the BLE device. How do you properly implement error handling from the users perspective when an unrelated operation can cause errors on another operation? Eg, I dont want to say there was an error setting up notifications if the real error was while calling `.setInterval`. – Orbit Dec 07 '16 at 22:06
  • Every operation causes exception specific to this operation. Usually when an error is happening during a BLE connection the connection is broken and needs to be recreated and every Observable from that connection is broken as well. The typical error handling when using an rx flow is all in one place where you check what error has happened. – Dariusz Seweryn Dec 08 '16 at 11:35

0 Answers0