Skip to content

Commit ffdb9b3

Browse files
committed
fix: make bluetooth and proximity manager runs on the main thread
1 parent 9561383 commit ffdb9b3

10 files changed

+468
-333
lines changed

android/src/main/java/com/zxcpoiu/incallmanager/AppRTC/AppRTCBluetoothManager.java

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
* in the file PATENTS. All contributing project authors may
88
* be found in the AUTHORS file in the root of the source tree.
99
*/
10-
1110
package com.zxcpoiu.incallmanager.AppRTC;
12-
1311
import android.annotation.SuppressLint;
1412
import android.bluetooth.BluetoothAdapter;
1513
import android.bluetooth.BluetoothDevice;
@@ -26,23 +24,22 @@
2624
import android.os.Looper;
2725
import android.os.Process;
2826
import android.util.Log;
27+
import androidx.annotation.Nullable;
2928
import java.util.List;
3029
import java.util.Set;
31-
30+
import com.zxcpoiu.incallmanager.AppRTC.AppRTCUtils;
31+
import com.zxcpoiu.incallmanager.AppRTC.ThreadUtils;
3232
import com.zxcpoiu.incallmanager.InCallManagerModule;
33-
3433
/**
3534
* AppRTCProximitySensor manages functions related to Bluetoth devices in the
3635
* AppRTC demo.
3736
*/
3837
public class AppRTCBluetoothManager {
3938
private static final String TAG = "AppRTCBluetoothManager";
40-
4139
// Timeout interval for starting or stopping audio to a Bluetooth SCO device.
4240
private static final int BLUETOOTH_SCO_TIMEOUT_MS = 4000;
4341
// Maximum number of SCO connection attempts.
4442
private static final int MAX_SCO_CONNECTION_ATTEMPTS = 2;
45-
4643
// Bluetooth connection state.
4744
public enum State {
4845
// Bluetooth is not available; no adapter or Bluetooth is off.
@@ -62,20 +59,21 @@ public enum State {
6259
// Bluetooth audio SCO connection with remote device is established.
6360
SCO_CONNECTED
6461
}
65-
6662
private final Context apprtcContext;
6763
private final InCallManagerModule apprtcAudioManager;
64+
@Nullable
6865
private final AudioManager audioManager;
6966
private final Handler handler;
70-
7167
int scoConnectionAttempts;
7268
private State bluetoothState;
7369
private final BluetoothProfile.ServiceListener bluetoothServiceListener;
70+
@Nullable
7471
private BluetoothAdapter bluetoothAdapter;
72+
@Nullable
7573
private BluetoothHeadset bluetoothHeadset;
74+
@Nullable
7675
private BluetoothDevice bluetoothDevice;
7776
private final BroadcastReceiver bluetoothHeadsetReceiver;
78-
7977
// Runs when the Bluetooth timeout expires. We use that timeout after calling
8078
// startScoAudio() or stopScoAudio() because we're not guaranteed to get a
8179
// callback after those calls.
@@ -85,7 +83,6 @@ public void run() {
8583
bluetoothTimeout();
8684
}
8785
};
88-
8986
/**
9087
* Implementation of an interface that notifies BluetoothProfile IPC clients when they have been
9188
* connected to or disconnected from the service.
@@ -105,7 +102,6 @@ public void onServiceConnected(int profile, BluetoothProfile proxy) {
105102
updateAudioDeviceState();
106103
Log.d(TAG, "onServiceConnected done: BT state=" + bluetoothState);
107104
}
108-
109105
@Override
110106
/** Notifies the client when the proxy object has been disconnected from the service. */
111107
public void onServiceDisconnected(int profile) {
@@ -121,7 +117,6 @@ public void onServiceDisconnected(int profile) {
121117
Log.d(TAG, "onServiceDisconnected done: BT state=" + bluetoothState);
122118
}
123119
}
124-
125120
// Intent broadcast receiver which handles changes in Bluetooth device availability.
126121
// Detects headset changes and Bluetooth SCO state changes.
127122
private class BluetoothHeadsetBroadcastReceiver extends BroadcastReceiver {
@@ -189,15 +184,14 @@ public void onReceive(Context context, Intent intent) {
189184
Log.d(TAG, "onReceive done: BT state=" + bluetoothState);
190185
}
191186
}
192-
193187
/** Construction. */
194188
public static AppRTCBluetoothManager create(Context context, InCallManagerModule audioManager) {
195-
Log.d(TAG, "create");
189+
Log.d(TAG, "create" + AppRTCUtils.getThreadInfo());
196190
return new AppRTCBluetoothManager(context, audioManager);
197191
}
198-
199192
protected AppRTCBluetoothManager(Context context, InCallManagerModule audioManager) {
200193
Log.d(TAG, "ctor");
194+
ThreadUtils.checkIsOnMainThread();
201195
apprtcContext = context;
202196
apprtcAudioManager = audioManager;
203197
this.audioManager = getAudioManager(context);
@@ -206,12 +200,11 @@ protected AppRTCBluetoothManager(Context context, InCallManagerModule audioManag
206200
bluetoothHeadsetReceiver = new BluetoothHeadsetBroadcastReceiver();
207201
handler = new Handler(Looper.getMainLooper());
208202
}
209-
210203
/** Returns the internal state. */
211204
public State getState() {
205+
ThreadUtils.checkIsOnMainThread();
212206
return bluetoothState;
213207
}
214-
215208
/**
216209
* Activates components required to detect Bluetooth devices and to enable
217210
* BT SCO (audio is routed via BT SCO) for the headset profile. The end
@@ -226,6 +219,7 @@ public State getState() {
226219
* change.
227220
*/
228221
public void start() {
222+
ThreadUtils.checkIsOnMainThread();
229223
Log.d(TAG, "start");
230224
String p = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? android.Manifest.permission.BLUETOOTH_CONNECT : android.Manifest.permission.BLUETOOTH;
231225
if (!hasPermission(apprtcContext, p)) {
@@ -271,9 +265,9 @@ public void start() {
271265
bluetoothState = State.HEADSET_UNAVAILABLE;
272266
Log.d(TAG, "start done: BT state=" + bluetoothState);
273267
}
274-
275268
/** Stops and closes all components related to Bluetooth audio. */
276269
public void stop() {
270+
ThreadUtils.checkIsOnMainThread();
277271
Log.d(TAG, "stop: BT state=" + bluetoothState);
278272
if (bluetoothAdapter == null) {
279273
return;
@@ -295,7 +289,6 @@ public void stop() {
295289
bluetoothState = State.UNINITIALIZED;
296290
Log.d(TAG, "stop done: BT state=" + bluetoothState);
297291
}
298-
299292
/**
300293
* Starts Bluetooth SCO connection with remote device.
301294
* Note that the phone application always has the priority on the usage of the SCO connection
@@ -310,6 +303,7 @@ public void stop() {
310303
* accept SCO audio without a "call".
311304
*/
312305
public boolean startScoAudio() {
306+
ThreadUtils.checkIsOnMainThread();
313307
Log.d(TAG, "startSco: BT state=" + bluetoothState + ", "
314308
+ "attempts: " + scoConnectionAttempts + ", "
315309
+ "SCO is on: " + isScoOn());
@@ -335,9 +329,9 @@ public boolean startScoAudio() {
335329
+ "SCO is on: " + isScoOn());
336330
return true;
337331
}
338-
339332
/** Stops Bluetooth SCO connection with remote device. */
340333
public void stopScoAudio() {
334+
ThreadUtils.checkIsOnMainThread();
341335
Log.d(TAG, "stopScoAudio: BT state=" + bluetoothState + ", "
342336
+ "SCO is on: " + isScoOn());
343337
if (bluetoothState != State.SCO_CONNECTING && bluetoothState != State.SCO_CONNECTED) {
@@ -350,12 +344,11 @@ public void stopScoAudio() {
350344
Log.d(TAG, "stopScoAudio done: BT state=" + bluetoothState + ", "
351345
+ "SCO is on: " + isScoOn());
352346
}
353-
354347
/**
355348
* Use the BluetoothHeadset proxy object (controls the Bluetooth Headset
356349
* Service via IPC) to update the list of connected devices for the HEADSET
357350
* profile. The internal state will change to HEADSET_UNAVAILABLE or to
358-
* HEADSET_AVAILABLE and |bluetoothDevice| will be mapped to the connected
351+
* HEADSET_AVAILABLE and `bluetoothDevice` will be mapped to the connected
359352
* device if available.
360353
*/
361354
public void updateDevice() {
@@ -382,32 +375,27 @@ public void updateDevice() {
382375
}
383376
Log.d(TAG, "updateDevice done: BT state=" + bluetoothState);
384377
}
385-
386378
/**
387379
* Stubs for test mocks.
388380
*/
381+
@Nullable
389382
protected AudioManager getAudioManager(Context context) {
390383
return (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
391384
}
392-
393385
protected void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
394386
apprtcContext.registerReceiver(receiver, filter);
395387
}
396-
397388
protected void unregisterReceiver(BroadcastReceiver receiver) {
398389
apprtcContext.unregisterReceiver(receiver);
399390
}
400-
401391
protected boolean getBluetoothProfileProxy(
402392
Context context, BluetoothProfile.ServiceListener listener, int profile) {
403393
return bluetoothAdapter.getProfileProxy(context, listener, profile);
404394
}
405-
406395
protected boolean hasPermission(Context context, String permission) {
407396
return apprtcContext.checkPermission(permission, Process.myPid(), Process.myUid())
408397
== PackageManager.PERMISSION_GRANTED;
409398
}
410-
411399
/** Logs the state of the local Bluetooth adapter. */
412400
@SuppressLint("HardwareIds")
413401
protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) {
@@ -425,30 +413,30 @@ protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) {
425413
}
426414
}
427415
}
428-
429416
/** Ensures that the audio manager updates its list of available audio devices. */
430417
private void updateAudioDeviceState() {
418+
ThreadUtils.checkIsOnMainThread();
431419
Log.d(TAG, "updateAudioDeviceState");
432420
apprtcAudioManager.updateAudioDeviceState();
433421
}
434-
435422
/** Starts timer which times out after BLUETOOTH_SCO_TIMEOUT_MS milliseconds. */
436423
private void startTimer() {
424+
ThreadUtils.checkIsOnMainThread();
437425
Log.d(TAG, "startTimer");
438426
handler.postDelayed(bluetoothTimeoutRunnable, BLUETOOTH_SCO_TIMEOUT_MS);
439427
}
440-
441428
/** Cancels any outstanding timer tasks. */
442429
private void cancelTimer() {
430+
ThreadUtils.checkIsOnMainThread();
443431
Log.d(TAG, "cancelTimer");
444432
handler.removeCallbacks(bluetoothTimeoutRunnable);
445433
}
446-
447434
/**
448435
* Called when start of the BT SCO channel takes too long time. Usually
449436
* happens when the BT device has been turned on during an ongoing call.
450437
*/
451438
private void bluetoothTimeout() {
439+
ThreadUtils.checkIsOnMainThread();
452440
if (bluetoothState == State.UNINITIALIZED || bluetoothHeadset == null) {
453441
return;
454442
}
@@ -482,12 +470,10 @@ private void bluetoothTimeout() {
482470
updateAudioDeviceState();
483471
Log.d(TAG, "bluetoothTimeout done: BT state=" + bluetoothState);
484472
}
485-
486473
/** Checks whether audio uses Bluetooth SCO. */
487474
private boolean isScoOn() {
488475
return audioManager.isBluetoothScoOn();
489476
}
490-
491477
/** Converts BluetoothAdapter states into local string representations. */
492478
private String stateToString(int state) {
493479
switch (state) {

0 commit comments

Comments
 (0)