2

I am using rxandroidble using autoconnect = true to continuously monitor data from a sensor. The app continuously scans for sensors it has previously connected to.

The data monitoring and scanning for sensors should continue all night, even if the phone is not connected to a power source.

If a sensor is connected in the evening, the app will stay connected all night, even if it disconnects momentarily.

However, if a sensor is disconnected for 6 hours during the night (because I pulled the sensor's battery), and then I reconnect the sensor battery in the morning, the phone does not appear to reconnect to the sensor.

I am constantly scanning every 8 seconds for new sensors in an Android service, but its not a WakefulService, nor is it started by a WakefulIntent. Should it be?

Any idea what could be going on? Is rxandroidble designed to keep scanning for sensors in this situation (ble device connects, goes out of range for 8 hours, then comes back into range)? Or do i need to manually attempt reconnect after disconnect, and then rxandroid will continually attempt to reconnect.

Here is my scan code:

    public boolean scanForDevices(boolean on){


    if(on){
        if (!mBluetoothAdapter.isEnabled()) {
            Log.e(TAG, "scanForDevices: bluetooth not enabled, scan failed" );
            return false; // bluetooth disabled
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

            if (TheApplication.getInstance().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                Log.e(TAG, "scanForDevices: ACCESS_COARSE_LOCATION permission not granted, scan failed" );
                return false; // App doesn't have permission granted
            }
        }

        if (isScanning()) {
            scanSubscription.unsubscribe();
        } else {

            scanSubscription = RxBleClientSingleton.getInstance().getRxBleClient().scanBleDevices()
                    .observeOn(AndroidSchedulers.mainThread())
                    .doOnError(this::onScanFailure)
                    .doOnUnsubscribe(this::clearSubscription)
                    .subscribe(this::onScanResult, this::onScanFailure);


            //Ensure we won't Scan forever (save battery)
            if (EnablePeriodicScan == true) {
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (isScanning())
                            scanForDevices(false);
                    }
                }, 4000);
            }

        }
    }else{
        //Turn scanning off
        if(isScanning()){
            scanSubscription.unsubscribe();
        }
    }
    return true;
}

and here is my connect code:

public void RxBleConnect() {



    autoConnectSetting = true;



    if (false == sensorConnectionState.equals(SensorConnectionState.Disconnected)) {
        Log.d(TAG, "RxBleConnect: cannot connect, #" + allowReconnect + " : " + macAddress + " isn't disconnected, currently in " + sensorConnectionState.toString() );

        if ((sensorConnectionState.equals(SensorConnectionState.Connected)) &&  (allowReconnect > 15)) { /// reached our limit even though we are connected?
            allowReconnect = 0; // reset count
            Disconnect(); // disconnect sensor.
            Log.d(TAG, "RxBleConnect: someone keeps knocking, appears that " + macAddress + " isn't as connected as we thought.");
        }
        allowReconnect ++;

        return; // dont reattempt connection if already attempting to connect
    }

    Log.d(TAG, "RxBleConnect: connecting to sensor....");

    SystemClock.sleep(400);


    connectionObservable  =
            bleDevice.establishConnection(TheApplication.getInstance(), autoConnectSetting)

            .compose(new ConnectionSharingAdapter())
            .observeOn(AndroidSchedulers.mainThread())


            .doOnUnsubscribe(this::RxBleDisconnect)
            .takeUntil(disconnectTriggerSubject)
            .doOnError(throwable -> {
                Log.d(TAG, "rxBleConnection doOnError: " + throwable);
                Disconnect(); // triggerDisconnect();
            });



    connectionSubscription = connectionObservable
            .flatMap(RxBleConnection::discoverServices)
            .subscribe(this::RxBleOnConnectionReceived, this::RxBleOnConnectionFailure);



}

If the connection state changes to disconnect, I don't attempt reconnect manually; either scanning or autoconnect should take care of this issue right?

I have turned off "App Optimization" aka Doze mode for the app in android battery settings, but I don't know if that is doing anything.

EDIT 1: Here are my logs.... we connect ok the first time, then we attempt connecting, then we immediately disconnect:

>>> FIRST CONNECTION <<<
12-09 21:23:46.904 8992-8992/appName D/PeripheralManager: RxBleConnect: connecting to sensor...., auto connect = false
onConnectionStateChange newState=2 status=0
12-09 21:23:09.298 8992-8992/appName D/PeripheralManager: onConnectionStateChange: new state RxBleConnectionState{CONNECTED}
12-09 21:23:46.831 8992-9004/appName D/BluetoothGatt: onClientConnectionState() - status=8 clientIf=5 device=E0:CF:8D:98:69:6A
12-09 21:23:46.832 8992-9004/appName D/RxBle#BluetoothGatt: onConnectionStateChange newState=0 status=8
12-09 21:23:46.897 8992-8992/appName D/PeripheralManager: onConnectionStateChange: new state RxBleConnectionState{DISCONNECTED}
12-09 21:23:46.897 8992-8992/appName D/Peripheral Manager: setConnectionState: Set e0:cf:8d:98:69:6a connection state to Disconnected
12-09 21:23:46.898 8992-8992/appName E/PeripheralManager: ### sendDisconnectNotification: mid = 2698601

>>> ATTEMPT RECONNECT HERE <<<<
12-09 21:23:46.904 8992-8992/appName D/PeripheralManager: RxBleConnect: connecting to sensor...., auto connect = true
12-09 21:23:46.915 8992-9004/appName D/RxBle#Radio:   QUEUED RxBleRadioOperationDisconnect(250971465)
12-09 21:23:46.916 8992-9070/appName D/RxBle#Radio:  STARTED RxBleRadioOperationDisconnect(250971465)
12-09 21:23:46.922 8992-9004/appName D/BluetoothGatt: setCharacteristicNotification() - uuid: f8c00003-159f-11e6-92f5-0002a5d5c51b enable: false
12-09 21:23:46.928 8992-9004/appName D/RxBle#Radio:   QUEUED RxBleRadioOperationDescriptorWrite(49261423)
12-09 21:23:47.306 8992-8992/appName D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(200794900)
12-09 21:23:47.316 8992-8992/appName D/PeripheralManager: onConnectionStateChange: new state RxBleConnectionState{CONNECTING}
12-09 21:23:47.316 8992-8992/appName D/Peripheral Manager: setConnectionState: Set e0:cf:8d:98:69:6a connection state to Connecting…
12-09 21:23:47.340 8992-8992/appName D/BluetoothManager: getConnectionState()
12-09 21:23:47.340 8992-8992/appName D/BluetoothManager: getConnectedDevices
12-09 21:23:47.347 8992-9070/appName D/RxBle#Radio: FINISHED RxBleRadioOperationDisconnect(250971465)
12-09 21:23:47.348 8992-9070/appName D/RxBle#Radio:  STARTED RxBleRadioOperationDescriptorWrite(49261423)
12-09 21:23:47.348 8992-8992/appName D/PeripheralManager: rxBleConnection doOnError: BleGattException{status=8, bleGattOperation=BleGattOperation{description='CONNECTION_STATE'}}
12-09 21:23:47.355 8992-8992/appName D/PeripheralManager: observeConnectionStateChanges FINISHED
12-09 21:23:47.355 8992-8992/appName D/PeripheralManager: rxBleConnection doOnError: BleGattException{status=8, bleGattOperation=BleGattOperation{description='CONNECTION_STATE'}}

Thanks!

2nd EDIT:

Code for consuming

private void RxBleOnConnectionReceived(RxBleDeviceServices services) { // discover services

    connectionsCount ++;

    Log.d(TAG, "RxBleOnConnectionReceived: Discovered services");


    setConnectionState(SensorConnectionState.Connected);

    try {



    connectionSubscription = connectionObservable
            .flatMap(rxBleConnection -> rxBleConnection.setupNotification(BluetoothLeUart.RX_UUID))
            .doOnError(throwable -> {
                Log.d(TAG, "RxBleOnConnectionReceived setupNotification doOnError: " + throwable);
                Disconnect(); 
            })
            .doOnNext(notificationObservable -> {
                // Notification has been set up
            })
            .flatMap(notificationObservable -> notificationObservable) // <-- Notification has been set up, now observe value changes.
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError(throwable -> {
                Log.d(TAG, "RxBleOnConnectionReceived doOnError: " + throwable);
            })
            .subscribe(
                    bytes -> {
                        // Given characteristic has been changes, here is the value.
                    processReceivedMessage(bytes);

                    },
                    throwable -> {
                        /*handle throwable*/

                        Log.e(TAG, "RxBleOnConnectionReceived: processReceivedMessage: " + throwable );
                    }
            );
seano
  • 21
  • 4

1 Answers1

1

There are several questions you have asked:

  • What service to use while the phone enters a doze mode?

The is a good question on stackoverflow already available. For instance here: How does doze mode affect background/foreground services, with/without partial/full wakelocks? There is also an issue in Cordova Plugin github page about using the BLE during doze: https://github.com/thaliproject/Thali_CordovaPlugin/issues/413

  • Is RxAndroidBle supposed to reconnect automatically if the device looses connection?

No. After the connection has ended (wether it was started using autoconnect = true or false) it has to be reestablished by calling RxBleDevice.establishConnection again. From the library description:

Auto connect

(...)

Auto connect concept may be misleading at first glance. With the autoconnect flag set to false the connection will end up with an error if a BLE device is not advertising when the RxBleDevice#establishConnection method is called. From platform to platform timeout after which the error is emitted differs, but in general it is rather tens of seconds than single seconds.

Setting the auto connect flag to true allows you to wait until the BLE device becomes discoverable. The RxBleConnection instance won't be emitted until the connection is fully set up. From experience it also handles acquiring wake locks, so it's safe to assume that your Android device will be woken up after the connection has been established - but it is not a documented feature and may change in the future system releases.

  • Will scanning or connecting with autoconnect = true take care of reconnecting?

It is hard to reason about what is triggering the scanForDevices() function but it looks like it automatically finishes after 4 seconds. If the scanning is on and the user would call scanForDevices(true) the scanning would be stopped anyway.

Community
  • 1
  • 1
Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21
  • Thank you very much. So does EstablishConnection continuously try to connect until it finds that device? or does it fail after a quick timeout? I guess ill try and report back unless you have a test result. And yes, scanForDevices is called after rxble initialization, and runs every 8 seconds, for a duration of 4 seconds. But i believe that it will be prevented from doing anything when the app enters deep sleep... – seano Dec 01 '16 at 10:22
  • I have edited the answer. Short story: `autoconnect = false` -> timeout after 30 seconds if device is not available, `autoconnect = true` -> no timeout until the device will be available. From here: https://github.com/thaliproject/Thali_CordovaPlugin/issues/413 you can read about possible scenarios with doze. Apparently it doesn't change much if the service is running. – Dariusz Seweryn Dec 01 '16 at 10:52
  • What response? If I didn't answer your question - please clarify the them for me or describe what you want to achieve. – Dariusz Seweryn Dec 09 '16 at 11:41
  • Sorry, my first SO post... As for my issue, I'm having instant autoconnect failure .... My code for Rxbleconnect is above. If I set autoconnect setting to true, the first connect succeeds, but then after the first DISconnect, if i try to connect again, RxAndroidBle immediately disconnects... it appears it is immediately entering the .doOnUnsubscribe portion of my establishConnection call... Any idea why? How do I ensure there is a subscriber? Is this because I separated the connectionObservable and the connectionSubscription into separate calls? (see code above) Thanks! – seano Dec 09 '16 at 13:34
  • If you use `Subscriber` subclass for consuming events then keep in mind that they are stateful. If one `Subscriber` recieves `onCompleted()` or `onError()` it will not work when subscribing again. It has to be a new `Subscriber`. Secondly `Observable` that is result of `RxBleDevice.establishConnection()` is meant to be used one time only. After it will disconnect or error another call to `RxBleDevice.establishConnection()` is needed. – Dariusz Seweryn Dec 10 '16 at 00:12
  • Thank you for your prompt replies. I am not using a Subscriber subclass. I'm calling subscribe after I figure out what services the device provides... See 2nd Edit above for the code... My situation is a lot like the one in http://stackoverflow.com/questions/38902913/rxandroidble-keeping-a-persistant-connection-write-notification-handling But i think my knowledge of rxjava is not sufficient. I wish there was a full reference design for this type of app for the library.... persistant IoT connections are more and more popular. I know that is quite a lot to ask so I wouldnt expect it.. – seano Dec 10 '16 at 07:22
  • I will think of a more complicated example with sustaining the connection and handling multiple (asynchronous) events during the time. Every use-case is different and without full knowledge of yours I cannot really help. As for now I can only advice you to extend your knowledge of rxJava - it will definitely benefit you. – Dariusz Seweryn Dec 13 '16 at 19:55