From 40d2bf0c2204b06bef1efef7ae9abdd6c82b0c90 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Thu, 5 May 2022 19:14:19 +0200 Subject: [PATCH] Bluetooth: use TrackRecording handler for callback. Otherwise the thread is not defined and a SecurityException may be triggered when inserting data into the ContentProvider. Sadly, below API26 this needs to be done manually. Fixes #1165. --- .../io/file/importer/ExportImportTest.java | 6 +++--- .../TrackRecordingServiceTestUtils.java | 2 +- .../handlers/TrackPointCreatorTest.java | 2 +- .../sensors/BluetoothConnectionManager.java | 18 ++++++++++++++++-- .../sensors/BluetoothRemoteSensorManager.java | 12 ++++++++++-- .../services/TrackRecordingService.java | 2 +- .../services/handlers/TrackPointCreator.java | 5 +++-- 7 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index c6c6841cf..fd3d6f3b3 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -162,7 +162,7 @@ public class ExportImportTest { service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null); trackPointCreator.setClock("2020-02-02T02:02:06Z"); - trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator)); + trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator)); service.pauseCurrentTrack(); trackPointCreator.setClock("2020-02-02T02:02:20Z"); @@ -175,7 +175,7 @@ public class ExportImportTest { sendLocation(trackPointCreator, "2020-02-02T02:02:23Z", 3, 16.001, 10, 27, 15, 10, 0); trackPointCreator.setClock("2020-02-02T02:02:24Z"); - trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator)); + trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator)); service.endCurrentTrack(); Track track = contentProviderUtils.getTrack(trackId); @@ -492,7 +492,7 @@ public class ExportImportTest { } private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) { - trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator) { + trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator) { @Override public SensorDataSet fill(@NonNull TrackPoint trackPoint) { SensorDataSet sensorDataSet = new SensorDataSet(); diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestUtils.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestUtils.java index 66d60e79f..14c45ecfa 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestUtils.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestUtils.java @@ -27,7 +27,7 @@ public class TrackRecordingServiceTestUtils { TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class))) .getService(); - service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, service.getTrackPointCreator())); + service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, service.getTrackPointCreator())); service.getTrackPointCreator().setClock(Clock.systemUTC()); service.endCurrentTrack(); } diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/handlers/TrackPointCreatorTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/handlers/TrackPointCreatorTest.java index 0768e0884..e10fb963e 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/handlers/TrackPointCreatorTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/handlers/TrackPointCreatorTest.java @@ -32,7 +32,7 @@ public class TrackPointCreatorTest { @Before public void setUp() { subject = new TrackPointCreator(locationHandler, server); - subject.start(context); + subject.start(context, null); } @After diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index 962b8cbbe..96905bf13 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -24,6 +24,8 @@ import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.os.Build; +import android.os.Handler; import android.util.Log; import androidx.annotation.NonNull; @@ -110,7 +112,12 @@ public abstract class BluetoothConnectionManager { SensorData sensorData = parsePayload(gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic); if (sensorData != null) { Log.d(TAG, "Decoded data from " + gatt.getDevice().getAddress() + ": " + sensorData); - observer.onChanged(sensorData); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + observer.onChanged(sensorData); + } else { + //TODO This might lead to NPEs in case of race conditions due to shutdown. + observer.getHandler().post(() -> observer.onChanged(sensorData)); + } } } }; @@ -128,7 +135,11 @@ public abstract class BluetoothConnectionManager { Log.d(TAG, "Connecting to: " + device); - bluetoothGatt = device.connectGatt(context, true, connectCallback); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + bluetoothGatt = device.connectGatt(context, true, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, this.observer.getHandler()); + } else { + bluetoothGatt = device.connectGatt(context, true, connectCallback); + } SensorData sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress()); observer.onChanged(sensorData); } @@ -275,5 +286,8 @@ public abstract class BluetoothConnectionManager { void onChanged(SensorData sensorData); void onDisconnecting(SensorData sensorData); + + @NonNull + Handler getHandler(); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index a9f025158..7ce4684c0 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.SharedPreferences; +import android.os.Handler; import android.util.Log; import androidx.annotation.NonNull; @@ -57,7 +58,7 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager. private final BluetoothAdapter bluetoothAdapter; private final Context context; - + private final Handler handler; private boolean started = false; private Distance preferenceWheelCircumference; @@ -111,8 +112,9 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager. } }; - public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull SensorDataSetChangeObserver observer) { + public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @NonNull SensorDataSetChangeObserver observer) { this.context = context; + this.handler = handler; this.observer = observer; bluetoothAdapter = BluetoothUtils.getAdapter(context); } @@ -218,6 +220,12 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager. sensorDataSet.remove(sensorData); } + @NonNull + @Override + public Handler getHandler() { + return handler; + } + public interface SensorDataSetChangeObserver { void onChange(SensorDataSet sensorDataSet); } diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index a3dcfc992..47968cf71 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -211,7 +211,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator. private void startSensors() { wakeLock = SystemUtils.acquireWakeLock(this, wakeLock); - trackPointCreator.start(this); + trackPointCreator.start(this, handler); showNotification(true); } diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java index 2f583ca2f..ec3ab5e95 100644 --- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java +++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java @@ -2,6 +2,7 @@ package de.dennisguse.opentracks.services.handlers; import android.content.Context; import android.location.Location; +import android.os.Handler; import android.util.Log; import android.util.Pair; @@ -48,12 +49,12 @@ public class TrackPointCreator implements BluetoothRemoteSensorManager.SensorDat this.gpsHandler = gpsHandler; } - public synchronized void start(@NonNull Context context) { + public synchronized void start(@NonNull Context context, @NonNull Handler handler) { this.context = context; gpsHandler.onStart(context); - remoteSensorManager = new BluetoothRemoteSensorManager(context, this); + remoteSensorManager = new BluetoothRemoteSensorManager(context, handler, this); altitudeSumManager = new AltitudeSumManager(); remoteSensorManager.start();