Connect never completes

Hi, splitting this out from https://mbientlab.com/community/discussion/2401/what-to-do-if-my-app-crashes-without-disconnecting-the-board#latest

I have code something like which never completes: 

private final bolts.Continuation<Void, Void> onConnected =
    new bolts.Continuation<Void, Void>() {

    @Override
    public Void then(Task<Void> task) throws Exception {
        if (task.isCancelled()) {
            Log.i(TAG, "Connection cancelled");
        } else if (task.isFaulted()) {
            Log.w(TAG, "Unable to connect", task.getError());
            board.connectAsync(1000).continueWith(this);
        } else {
            Log.i(TAG, "Connected");
            /* Do connection tasks */    
                       
        }
        return null;
    }
};

board = service.getMetaWearBoard(btManager.getAdapter().getRemoteDevice(mac));
board.onUnexpectedDisconnect(status -> {
    Log.i(TAG, "Unexpected Disconnect");
    board.connectAsync(1000).continueWith(onConnected);
});
board.connectAsync().continueWith(onConnected);

Comments

  • I had a bit of a look through some of the Metawear SDK and I narrowed it down to public Task<Void> remoteDisconnectAsync() in BtleService, disconnectTaskSrc never completes. I've temporarily made this a TimedTask so that it will at least time out.
  • Thank you, I had a quick test and it worked. I'm now doing a live test and I'll let you know if I find any issues.
  • Hello,
    I'm using binary version of the API. When the fix will be available?
    Thanks
  • Luca said:

    Hello,
    I'm using binary version of the API. When the fix will be available?
    Thanks

    Hello, like @Luca we are using the binary version of the library and it would be important to have this fixed asap.
    Thanks
  • edited February 2018
    Have you tested the previously linked commit with your apps? i would like to know if the fixes work for you before releasing it.
  • I've tested my application using the linked commit and the problem is still there.
    Samsung Galaxy Tab S2 running Android Nougat (7.0).

  • Post the code you are testing with and provide instructions on how to get the API to hang on connectAsync.

  • package it.beonsolutions.rehabhome;

    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.bluetooth.BluetoothDevice;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.content.SharedPreferences;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.preference.PreferenceManager;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;

    import com.mbientlab.bletoolbox.scanner.BleScannerFragment;
    import com.mbientlab.metawear.MetaWearBoard;
    import com.mbientlab.metawear.android.BtleService;
    import com.mbientlab.metawear.module.Debug;
    import com.mbientlab.metawear.module.Settings;

    import java.util.UUID;

    import bolts.Continuation;
    import bolts.Task;

    public class SearchSensorActivity extends Activity implements ServiceConnection, BleScannerFragment.ScannerCommunicationBus {

    /**
     * Key to read back the output
     */
    public static final String PARAM_SELECTED_DEVICE = "BT_DEVICE";
    
    private static final String LOG_TAG = "SENSOR";
    private BtleService.LocalBinder serviceBinder;
    private MetaWearBoard metawear;
    private BluetoothDevice btDevice;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            setContentView(R.layout.activity_main);
        } else {
            setContentView(R.layout.activity_main_emu);
        }
    
        // Bind the mbientlab service when the activity is created
        getApplicationContext().bindService(new Intent(this, BtleService.class),
                this, Context.BIND_AUTO_CREATE);
    
    }
    
    // -------------- ServiceConnection implementation ---------------------------------
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // Typecast the binder to the service's LocalBinder class
        serviceBinder = (BtleService.LocalBinder) service;
    
    }
    
    @Override
    public void onServiceDisconnected(ComponentName componentName) { }
    
    
    
    // ---------- BleScannerFragment.ScannerCommunicationBus implementation ---------------------
    
    @Override
    public UUID[] getFilterServiceUuids() {
        // copiato da app "starter"
        return new UUID[] {MetaWearBoard.METAWEAR_GATT_SERVICE};
    }
    
    @Override
    public long getScanDuration() {
        return 10000L;
    }
    
    /**
     * Called when the user has selected a Bluetooth device from the device list.
     * Copied from the starter mbientlab app
     *
     * @param device Device the user selected
     */
    @Override
    public void onDeviceSelected(BluetoothDevice device) {
        metawear = serviceBinder.getMetaWearBoard(device);
        btDevice = device;
        final ProgressDialog connectDialog = new ProgressDialog(this);
        connectDialog.setTitle(getString(R.string.title_connecting));
        connectDialog.setMessage(getString(R.string.message_wait));
        connectDialog.setCancelable(false);
        connectDialog.setCanceledOnTouchOutside(false);
        connectDialog.setIndeterminate(true);
        connectDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
                getString(android.R.string.cancel),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        metawear.disconnectAsync();
                    }
                });
        connectDialog.show();
    
        metawear.connectAsync().continueWithTask(task -> {
            if (task.isCancelled()) {
                Log.i(LOG_TAG, "onDeviceSelected: task was cancelled");
                return task;
            }
            Task<Void> t2;
            if (task.isFaulted()) {
                Log.e(LOG_TAG, "onDeviceSelected: task failed", task.getError());
                t2 = reconnect(metawear);
            } else {
                t2 = Task.forResult(null);
            }
            return t2;
        })
                .continueWith(task -> {
                    if (!task.isCancelled()) {
                        //setConnInterval(metawear.getModule(Settings.class));
                        runOnUiThread(connectDialog::dismiss);
                        returnToCaller();
                    }
                    return null;
                });
    }
    

    // static void setConnInterval(Settings settings) {
    // Settings.BleConnectionParametersEditor editor = settings.editBleConnParams();
    // if (editor != null) {
    // editor.maxConnectionInterval(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? 11.25f : 7.5f)
    // .commit();
    // }
    // }
    private void returnToCaller() {

        if (btDevice == null) {
            Log.w(LOG_TAG, "returnToCaller: btDevice is null!");
            setResult(RESULT_CANCELED);
        } else {
            Intent resIntent = new Intent();
            resIntent.putExtra(PARAM_SELECTED_DEVICE, btDevice);
            setResult(RESULT_OK, resIntent);
        }
        finish();
    }
    
    
    /**
     * Handle the reconnection after a dropped BT connection.
     * Copied from the starter mbientlab app
     * @param board
     * @return
     */
    public static Task<Void> reconnect(final MetaWearBoard board) {
        return board.connectAsync()
                .continueWithTask(task -> {
                    if (task.isFaulted()) {
                        return reconnect(board);
                    } else if (task.isCancelled()) {
                        return task;
                    }
                    return Task.forResult(null);
                });
    }
    
    
    @Override
    public void onDestroy() {
        try {
            getApplicationContext().unbindService(this);
        } catch (Exception e) {
            Log.e(LOG_TAG, "onDestroy: ",e);
        } finally {
            super.onDestroy();
        }
    
    }
    

    }

  • ___________________activity_search_sensor.xml______________________________________

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="it.beonsolutions.rehabhome.SearchSensorActivity">

    <TextView
        android:id="@+id/textInstructions"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="@string/home_help_select_sensor" />
    
    <fragment
        android:id="@+id/fragment"
        android:name="com.mbientlab.bletoolbox.scanner.BleScannerFragment"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="0.75"
        tools:layout="@layout/blescan_device_list"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/textInstructions"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="64dp" />
    

    </android.support.constraint.ConstraintLayout>

  • The above code runs fine on Lenovo Tab 3 730X, but makes an infinite wait on HUAWEI Mediapad T3 7 (Android 7)

  • Also your MetaBase app refuses to connect on the HUAWEI

  • Based on the above code, it seems like the reconnect attempts continuously fail, not that connectAsync never terminates.

    Regarding the Huawei device, try to connect to devices with another BLE app, like the nRF Connect app, and also post the errors returned from the (re)connect attempts.

  • The above code, as you surely see, is the one you showcase in the tutorial/starter sample. If that is not the best connection code skeleton, please update it to a better one.

    Why you don't modify your MetaBase app, so that it collects and sends error/debugging information, that can be used by you to debug your API?

  • edited March 2018

    I simply pointed out the the likely root cause of this infinite wait you are seeing on the Huawei device, not that there is anything wrong with the code. Furthermore, the tutorials are to help you get started; it is up to you if you want to continue using the code or modify it to fit your app flow.

    The starter app code includes a cancel button so you can cancel the connect attempt if it is taking too long.



    I am not talking about your Huawei device failing to connect in MetaBase. You stated the device cannot connect with your app; the next logical step is to see if that device can even establish a connection with a generic BLE app. A quick search on Google shows that people have issues with Huawai devices on other platforms, such as the FitBit devices, so this seems like an issue that Huawei needs to address.

    MetaBase already collects error information however, as previously state, this seems like an issue with the Huawei device.

  • I see. Thanks for the explanations. I wasn't reasoning along those lines, because:
    a) sometimes it connects, sometimes it doesn't (however we have a bunch of sensor that we use for testing; not sure if the different behavior is due to different sensors)
    b) we tested the software on other Huawei devices and had no issues.

    However I understand your point and will try to send the information you requested (or eventually give up with Huawei...)

  • I made an attempt with nRF connect.
    It does connect without any issue. I'm attaching a picture and a log file from nRFconnect

  • Eric,
    I tried to gather error messages from the task returned from the connectAsync method, but the error handling code is never called. It appears to be stuck in connectAsync; the statament

    Log.e(LOG_TAG, "onDeviceSelected: task failed", task.getError());

    never gets called.

    I'm attaching my code.

  • UPDATE: I've got this error:

    E/SENSOR: onDeviceSelected: task failed
    java.util.concurrent.TimeoutException: onCharacteristicRead not called within 1000ms
    at com.mbientlab.metawear.impl.platform.TimedTask.lambda$execute$0$TimedTask(TimedTask.java:35)
    at com.mbientlab.metawear.impl.platform.TimedTask$$Lambda$0.then(Unknown Source)
    at bolts.Task$14.run(Task.java:872)
    at bolts.BoltsExecutors$ImmediateExecutor.execute(BoltsExecutors.java:105)
    at bolts.Task.completeImmediately(Task.java:863)
    at bolts.Task.access$000(Task.java:32)
    at bolts.Task$10.then(Task.java:654)
    at bolts.Task$10.then(Task.java:651)
    at bolts.Task.runContinuations(Task.java:956)
    ...

    The weird thing is that if make a new connection attempt, it never ends

  • At that point, if I try again with MetaBase, I get:

    If I select Yes, the connection fails .
    If I select Cancel, then try again, the connection succeeds.

  • Almost the same happens to me too. Unfortunately the code is too complex to be posted piece by piece giving it a sense. And I can not send the whole project.

    What I observed while developing is that, if the application crashes while connected to MetaWear boards streaming gyro and accelerometer data, when I restart the application often, not always, the connection procedure blocks without giving any timeout. As like as @Luca, If I restart again the application, everything comes back to work.

  • And to confirm, the TimeoutException you posted above is still occurring with the API variant posted in this thread?

    The read characteristic error is a good starting point; I will try to reproduce it on my side.

    Hrm, this issue with connectAsync never completing after a crash is very odd. Are you sure connectAsync is in fact hanging and the code is not attempting to unsuccessfully reconnect?

  • @Eric said:
    And to confirm, the TimeoutException you posted above is still occurring with the API variant posted in this thread?

    Yes, with the latest code I posted in the .zip attachment.

    Hrm, this issue with connectAsync never completing after a crash is very odd. Are you sure connectAsync is in fact hanging and the code is not attempting to unsuccessfully reconnect?

    Yes, I'm sure. I was debugging it with AndroidStudio and had a breakpoint on line 116 (the one that goes like:
    if (task.isCancelled()) {
    )
    and it never reached the breakpoint.

  • edited March 2018

    I have not been able to reproduce either issue with our Android devices.

    For the stack trace issue regarding the onCharacteristicRead callback function, I forced the API into throwing that exception by never reading any characteristics. connectAsync failed, as expected, and continuously failed with every reconnect attempt.

    When testing the connect - streaming issue, the only issue I ever encountered was a non-zero connect status, which immediately competed the connectAsync task. I was streaming both gyro and acc at 100Hz for this test.

This discussion has been closed.