Refactor Bluetooth LE stack to use autoReconnect.

Removed additional timerTask to reconnect (was not working anymore).

Fixed #41.
This commit is contained in:
Dennis Guse
2019-10-26 13:55:02 +02:00
parent e9d5718495
commit 7d807a7f11
5 changed files with 85 additions and 136 deletions
@@ -8,6 +8,7 @@ public enum SensorState {
NONE,
CONNECTING,
CONNECTED,
DISCONNECTING,
DISCONNECTED;
public static String getStateAsString(SensorState state, Context c) {
@@ -18,6 +19,8 @@ public enum SensorState {
return c.getString(R.string.sensor_state_connecting);
case CONNECTED:
return c.getString(R.string.sensor_state_connected);
case DISCONNECTING:
return c.getString(R.string.sensor_state_disconnected);
case DISCONNECTED:
return c.getString(R.string.sensor_state_disconnected);
default:
@@ -58,22 +58,37 @@ public class BluetoothConnectionManager {
private SensorState sensorState;
private BluetoothGatt bluetoothGatt;
private BluetoothDevice bluetoothDevice;
private BluetoothGattCallback connectCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
setState(SensorState.CONNECTED);
switch (newState) {
case BluetoothProfile.STATE_CONNECTING:
Log.d(TAG, "Connecting to sensor: " + gatt.getDevice());
setState(SensorState.CONNECTING);
case BluetoothProfile.STATE_CONNECTED:
Log.d(TAG, "Connected to sensor: " + gatt.getDevice());
setState(SensorState.CONNECTED);
//Inform about status change
Message message = handler.obtainMessage(MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(KEY_DEVICE_NAME, gatt.getDevice().getName());
message.setData(bundle);
handler.sendMessage(message);
return;
gatt.discoverServices();
//Inform about status change
Message message = handler.obtainMessage(MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(KEY_DEVICE_NAME, gatt.getDevice().getName());
message.setData(bundle);
handler.sendMessage(message);
break;
case BluetoothProfile.STATE_DISCONNECTING:
Log.d(TAG, "Disconnecting from sensor: " + gatt.getDevice());
setState(SensorState.DISCONNECTING);
case BluetoothProfile.STATE_DISCONNECTED:
Log.d(TAG, "Disconnected from sensor: " + gatt.getDevice());
setState(SensorState.DISCONNECTED);
}
Log.d(TAG, "Could not connect to bluetooth sensor: " + gatt.getDevice());
}
@Override
@@ -104,45 +119,42 @@ public class BluetoothConnectionManager {
*
* @param handler a handler for sending messages back to the UI activity
*/
BluetoothConnectionManager(Context context, Handler handler) {
BluetoothConnectionManager(@NonNull Context context, @NonNull BluetoothDevice bluetoothDevice, @NonNull Handler handler) {
this.context = context;
this.bluetoothDevice = bluetoothDevice;
this.handler = handler;
this.sensorState = SensorState.NONE;
}
/**
* Gets the sensor state.
*/
public synchronized void connect() {
if (bluetoothGatt != null) {
Log.w(TAG, "Already connected; ignoring.");
}
Log.d(TAG, "Connecting to: " + bluetoothDevice);
setState(SensorState.CONNECTING);
bluetoothGatt = bluetoothDevice.connectGatt(this.context, true, this.connectCallback);
}
public synchronized void disconnect() {
if (bluetoothGatt == null) {
Log.w(TAG, "Cannot disconnect if not connected.");
return;
}
bluetoothGatt.close();
bluetoothGatt = null;
}
public synchronized boolean isSameBluetoothDevice(BluetoothDevice bluetoothDevice) {
return this.bluetoothDevice.equals(bluetoothDevice);
}
synchronized SensorState getSensorState() {
return sensorState;
}
/**
* Sets the sensor state.
*
* @param sensorState the sensor state
*/
private synchronized void setState(SensorState sensorState) {
this.sensorState = sensorState;
}
/**
* Resets the bluetooth connection manager.
*/
public synchronized void reset() {
//TODO Disconnect
setState(SensorState.NONE);
}
/**
* Connects to a bluetooth device.
*
* @param bluetoothDevice the bluetooth device
*/
public synchronized void connect(BluetoothDevice bluetoothDevice) {
Log.d(TAG, "connect to: " + bluetoothDevice);
bluetoothDevice.connectGatt(this.context, false, this.connectCallback);
setState(SensorState.CONNECTING);
}
}
@@ -23,18 +23,17 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.content.sensor.SensorState;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.UnitConversions;
/**
* Bluetooth sensor manager.
* TODO: Handle a BluetoothGatt.STATE_DISCONNECTED
*
* @author Sandor Dornbush
*/
@@ -46,10 +45,6 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
private final Context context;
private final BluetoothConnectionManager bluetoothConnectionManager;
private SensorDataSet sensorDataSet = null;
// Handler that gets information back from the bluetoothConnectionManager
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -57,6 +52,8 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
switch (message.what) {
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
String deviceName = message.getData().getString(BluetoothConnectionManager.KEY_DEVICE_NAME);
String toastMessage = context.getString(R.string.settings_sensor_connected, deviceName);
Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show();
break;
case BluetoothConnectionManager.MESSAGE_READ:
if (!(message.obj instanceof SensorDataSet)) {
@@ -72,12 +69,14 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
}
};
private SensorDataSet sensorDataSet = null;
private BluetoothConnectionManager bluetoothConnectionManager;
/**
* @param context the context
*/
BluetoothRemoteSensorManager(Context context) {
this.context = context;
bluetoothConnectionManager = new BluetoothConnectionManager(context, messageHandler);
}
private static BluetoothAdapter getDefaultBluetoothAdapter() {
@@ -118,12 +117,7 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
}
@Override
public boolean isEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}
@Override
protected void setUpChannel() {
public void startSensor() {
if (!isEnabled()) {
Log.w(TAG, "Bluetooth not enabled.");
return;
@@ -134,26 +128,37 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
Log.w(TAG, "No bluetooth address.");
return;
}
Log.w(TAG, "Connecting to bluetooth address: " + address);
Log.i(TAG, "Connecting to bluetooth address: " + address);
BluetoothDevice device;
try {
device = bluetoothAdapter.getRemoteDevice(address);
} catch (IllegalArgumentException e) {
Log.d(TAG, "Unable to get remote device for: " + address, e);
Log.w(TAG, "Unable to get remote device for: " + address, e);
String toastMessage = context.getString(R.string.sensor_not_known, address);
Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show();
return;
}
bluetoothConnectionManager.connect(device);
stopSensor();
bluetoothConnectionManager = new BluetoothConnectionManager(context, device, messageHandler);
bluetoothConnectionManager.connect();
}
@Override
protected void tearDownChannel() {
bluetoothConnectionManager.reset();
public void stopSensor() {
if (bluetoothConnectionManager != null) {
bluetoothConnectionManager.disconnect();
bluetoothConnectionManager = null;
}
}
@Override
public SensorState getSensorState() {
return bluetoothConnectionManager.getSensorState();
public boolean isEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}
@Override
@@ -16,13 +16,7 @@
package de.dennisguse.opentracks.services.sensors;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.content.sensor.SensorState;
/**
* Manage the connection to a remote sensor.
@@ -32,32 +26,14 @@ import de.dennisguse.opentracks.content.sensor.SensorState;
public abstract class RemoteSensorManager {
public static final long MAX_SENSOR_DATE_SET_AGE_MS = 5000;
public static final long MAX_SENSOR_STATE_AGE_MS = 20000;
private static final String TAG = RemoteSensorManager.class.getSimpleName();
private static final int RETRY_PERIOD_MS = 20000;
private SensorState sensorState = SensorState.NONE;
private long sensorStateTimestamp_ms = System.currentTimeMillis();
private TimerTask timerTask;
private Timer timer;
/**
* Returns true if the sensor is enabled.
*/
public abstract boolean isEnabled();
/**
* Sets up the sensor channel.
*/
protected abstract void setUpChannel();
/**
* Tears down the sensor channel.
*/
protected abstract void tearDownChannel();
/**
* Gets the sensor data set.
*/
@@ -66,61 +42,12 @@ public abstract class RemoteSensorManager {
/**
* Starts the sensor.
*/
public void startSensor() {
setUpChannel();
timerTask = new TimerTask() {
@Override
public void run() {
switch (getSensorState()) {
case CONNECTING:
if (System.currentTimeMillis() - sensorStateTimestamp_ms > MAX_SENSOR_STATE_AGE_MS) {
Log.i(TAG, "Retry setUpChannel");
setUpChannel();
}
break;
case DISCONNECTED:
setUpChannel();
break;
default: // NONE, CONNECTED or SENDING
}
}
};
timer = new Timer(RemoteSensorManager.class.getSimpleName());
timer.schedule(timerTask, RETRY_PERIOD_MS, RETRY_PERIOD_MS);
}
public abstract void startSensor();
/**
* Stops the sensor.
*/
public void stopSensor() {
if (timerTask != null) {
timerTask.cancel();
timerTask = null;
}
if (timer != null) {
timer.cancel();
timer.purge();
timer = null;
}
tearDownChannel();
}
/**
* Gets the sensor state.
*/
public SensorState getSensorState() {
return sensorState;
}
/**
* Sets the sensor state.
*
* @param sensorState the sensor state
*/
public void setSensorState(SensorState sensorState) {
sensorStateTimestamp_ms = System.currentTimeMillis();
this.sensorState = sensorState;
}
public abstract void stopSensor();
/**
* Returns true if the sensor data set is valid.
+3 -1
View File
@@ -1054,7 +1054,9 @@ limitations under the License.
sending data.">
Sending
</string>
<string name="sensor_not_known" translation_description="Error message if sensor is not available in Android (e.g., pairing was removed in Bluetooth Settings).">
Sensor %1$s is not known. Please check the settings.
</string>
<!-- Settings -->
<string name="settings_not_while_recording" translation_description="In settings, the summary for an option that is not available during a
track recording.">