7
7
* in the file PATENTS. All contributing project authors may
8
8
* be found in the AUTHORS file in the root of the source tree.
9
9
*/
10
-
11
10
package com .zxcpoiu .incallmanager .AppRTC ;
12
-
13
11
import android .annotation .SuppressLint ;
14
12
import android .bluetooth .BluetoothAdapter ;
15
13
import android .bluetooth .BluetoothDevice ;
26
24
import android .os .Looper ;
27
25
import android .os .Process ;
28
26
import android .util .Log ;
27
+ import androidx .annotation .Nullable ;
29
28
import java .util .List ;
30
29
import java .util .Set ;
31
-
30
+ import com .zxcpoiu .incallmanager .AppRTC .AppRTCUtils ;
31
+ import com .zxcpoiu .incallmanager .AppRTC .ThreadUtils ;
32
32
import com .zxcpoiu .incallmanager .InCallManagerModule ;
33
-
34
33
/**
35
34
* AppRTCProximitySensor manages functions related to Bluetoth devices in the
36
35
* AppRTC demo.
37
36
*/
38
37
public class AppRTCBluetoothManager {
39
38
private static final String TAG = "AppRTCBluetoothManager" ;
40
-
41
39
// Timeout interval for starting or stopping audio to a Bluetooth SCO device.
42
40
private static final int BLUETOOTH_SCO_TIMEOUT_MS = 4000 ;
43
41
// Maximum number of SCO connection attempts.
44
42
private static final int MAX_SCO_CONNECTION_ATTEMPTS = 2 ;
45
-
46
43
// Bluetooth connection state.
47
44
public enum State {
48
45
// Bluetooth is not available; no adapter or Bluetooth is off.
@@ -62,20 +59,21 @@ public enum State {
62
59
// Bluetooth audio SCO connection with remote device is established.
63
60
SCO_CONNECTED
64
61
}
65
-
66
62
private final Context apprtcContext ;
67
63
private final InCallManagerModule apprtcAudioManager ;
64
+ @ Nullable
68
65
private final AudioManager audioManager ;
69
66
private final Handler handler ;
70
-
71
67
int scoConnectionAttempts ;
72
68
private State bluetoothState ;
73
69
private final BluetoothProfile .ServiceListener bluetoothServiceListener ;
70
+ @ Nullable
74
71
private BluetoothAdapter bluetoothAdapter ;
72
+ @ Nullable
75
73
private BluetoothHeadset bluetoothHeadset ;
74
+ @ Nullable
76
75
private BluetoothDevice bluetoothDevice ;
77
76
private final BroadcastReceiver bluetoothHeadsetReceiver ;
78
-
79
77
// Runs when the Bluetooth timeout expires. We use that timeout after calling
80
78
// startScoAudio() or stopScoAudio() because we're not guaranteed to get a
81
79
// callback after those calls.
@@ -85,7 +83,6 @@ public void run() {
85
83
bluetoothTimeout ();
86
84
}
87
85
};
88
-
89
86
/**
90
87
* Implementation of an interface that notifies BluetoothProfile IPC clients when they have been
91
88
* connected to or disconnected from the service.
@@ -105,7 +102,6 @@ public void onServiceConnected(int profile, BluetoothProfile proxy) {
105
102
updateAudioDeviceState ();
106
103
Log .d (TAG , "onServiceConnected done: BT state=" + bluetoothState );
107
104
}
108
-
109
105
@ Override
110
106
/** Notifies the client when the proxy object has been disconnected from the service. */
111
107
public void onServiceDisconnected (int profile ) {
@@ -121,7 +117,6 @@ public void onServiceDisconnected(int profile) {
121
117
Log .d (TAG , "onServiceDisconnected done: BT state=" + bluetoothState );
122
118
}
123
119
}
124
-
125
120
// Intent broadcast receiver which handles changes in Bluetooth device availability.
126
121
// Detects headset changes and Bluetooth SCO state changes.
127
122
private class BluetoothHeadsetBroadcastReceiver extends BroadcastReceiver {
@@ -189,15 +184,14 @@ public void onReceive(Context context, Intent intent) {
189
184
Log .d (TAG , "onReceive done: BT state=" + bluetoothState );
190
185
}
191
186
}
192
-
193
187
/** Construction. */
194
188
public static AppRTCBluetoothManager create (Context context , InCallManagerModule audioManager ) {
195
- Log .d (TAG , "create" );
189
+ Log .d (TAG , "create" + AppRTCUtils . getThreadInfo () );
196
190
return new AppRTCBluetoothManager (context , audioManager );
197
191
}
198
-
199
192
protected AppRTCBluetoothManager (Context context , InCallManagerModule audioManager ) {
200
193
Log .d (TAG , "ctor" );
194
+ ThreadUtils .checkIsOnMainThread ();
201
195
apprtcContext = context ;
202
196
apprtcAudioManager = audioManager ;
203
197
this .audioManager = getAudioManager (context );
@@ -206,12 +200,11 @@ protected AppRTCBluetoothManager(Context context, InCallManagerModule audioManag
206
200
bluetoothHeadsetReceiver = new BluetoothHeadsetBroadcastReceiver ();
207
201
handler = new Handler (Looper .getMainLooper ());
208
202
}
209
-
210
203
/** Returns the internal state. */
211
204
public State getState () {
205
+ ThreadUtils .checkIsOnMainThread ();
212
206
return bluetoothState ;
213
207
}
214
-
215
208
/**
216
209
* Activates components required to detect Bluetooth devices and to enable
217
210
* BT SCO (audio is routed via BT SCO) for the headset profile. The end
@@ -226,6 +219,7 @@ public State getState() {
226
219
* change.
227
220
*/
228
221
public void start () {
222
+ ThreadUtils .checkIsOnMainThread ();
229
223
Log .d (TAG , "start" );
230
224
String p = Build .VERSION .SDK_INT >= Build .VERSION_CODES .S ? android .Manifest .permission .BLUETOOTH_CONNECT : android .Manifest .permission .BLUETOOTH ;
231
225
if (!hasPermission (apprtcContext , p )) {
@@ -271,9 +265,9 @@ public void start() {
271
265
bluetoothState = State .HEADSET_UNAVAILABLE ;
272
266
Log .d (TAG , "start done: BT state=" + bluetoothState );
273
267
}
274
-
275
268
/** Stops and closes all components related to Bluetooth audio. */
276
269
public void stop () {
270
+ ThreadUtils .checkIsOnMainThread ();
277
271
Log .d (TAG , "stop: BT state=" + bluetoothState );
278
272
if (bluetoothAdapter == null ) {
279
273
return ;
@@ -295,7 +289,6 @@ public void stop() {
295
289
bluetoothState = State .UNINITIALIZED ;
296
290
Log .d (TAG , "stop done: BT state=" + bluetoothState );
297
291
}
298
-
299
292
/**
300
293
* Starts Bluetooth SCO connection with remote device.
301
294
* Note that the phone application always has the priority on the usage of the SCO connection
@@ -310,6 +303,7 @@ public void stop() {
310
303
* accept SCO audio without a "call".
311
304
*/
312
305
public boolean startScoAudio () {
306
+ ThreadUtils .checkIsOnMainThread ();
313
307
Log .d (TAG , "startSco: BT state=" + bluetoothState + ", "
314
308
+ "attempts: " + scoConnectionAttempts + ", "
315
309
+ "SCO is on: " + isScoOn ());
@@ -335,9 +329,9 @@ public boolean startScoAudio() {
335
329
+ "SCO is on: " + isScoOn ());
336
330
return true ;
337
331
}
338
-
339
332
/** Stops Bluetooth SCO connection with remote device. */
340
333
public void stopScoAudio () {
334
+ ThreadUtils .checkIsOnMainThread ();
341
335
Log .d (TAG , "stopScoAudio: BT state=" + bluetoothState + ", "
342
336
+ "SCO is on: " + isScoOn ());
343
337
if (bluetoothState != State .SCO_CONNECTING && bluetoothState != State .SCO_CONNECTED ) {
@@ -350,12 +344,11 @@ public void stopScoAudio() {
350
344
Log .d (TAG , "stopScoAudio done: BT state=" + bluetoothState + ", "
351
345
+ "SCO is on: " + isScoOn ());
352
346
}
353
-
354
347
/**
355
348
* Use the BluetoothHeadset proxy object (controls the Bluetooth Headset
356
349
* Service via IPC) to update the list of connected devices for the HEADSET
357
350
* 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
359
352
* device if available.
360
353
*/
361
354
public void updateDevice () {
@@ -382,32 +375,27 @@ public void updateDevice() {
382
375
}
383
376
Log .d (TAG , "updateDevice done: BT state=" + bluetoothState );
384
377
}
385
-
386
378
/**
387
379
* Stubs for test mocks.
388
380
*/
381
+ @ Nullable
389
382
protected AudioManager getAudioManager (Context context ) {
390
383
return (AudioManager ) context .getSystemService (Context .AUDIO_SERVICE );
391
384
}
392
-
393
385
protected void registerReceiver (BroadcastReceiver receiver , IntentFilter filter ) {
394
386
apprtcContext .registerReceiver (receiver , filter );
395
387
}
396
-
397
388
protected void unregisterReceiver (BroadcastReceiver receiver ) {
398
389
apprtcContext .unregisterReceiver (receiver );
399
390
}
400
-
401
391
protected boolean getBluetoothProfileProxy (
402
392
Context context , BluetoothProfile .ServiceListener listener , int profile ) {
403
393
return bluetoothAdapter .getProfileProxy (context , listener , profile );
404
394
}
405
-
406
395
protected boolean hasPermission (Context context , String permission ) {
407
396
return apprtcContext .checkPermission (permission , Process .myPid (), Process .myUid ())
408
397
== PackageManager .PERMISSION_GRANTED ;
409
398
}
410
-
411
399
/** Logs the state of the local Bluetooth adapter. */
412
400
@ SuppressLint ("HardwareIds" )
413
401
protected void logBluetoothAdapterInfo (BluetoothAdapter localAdapter ) {
@@ -425,30 +413,30 @@ protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) {
425
413
}
426
414
}
427
415
}
428
-
429
416
/** Ensures that the audio manager updates its list of available audio devices. */
430
417
private void updateAudioDeviceState () {
418
+ ThreadUtils .checkIsOnMainThread ();
431
419
Log .d (TAG , "updateAudioDeviceState" );
432
420
apprtcAudioManager .updateAudioDeviceState ();
433
421
}
434
-
435
422
/** Starts timer which times out after BLUETOOTH_SCO_TIMEOUT_MS milliseconds. */
436
423
private void startTimer () {
424
+ ThreadUtils .checkIsOnMainThread ();
437
425
Log .d (TAG , "startTimer" );
438
426
handler .postDelayed (bluetoothTimeoutRunnable , BLUETOOTH_SCO_TIMEOUT_MS );
439
427
}
440
-
441
428
/** Cancels any outstanding timer tasks. */
442
429
private void cancelTimer () {
430
+ ThreadUtils .checkIsOnMainThread ();
443
431
Log .d (TAG , "cancelTimer" );
444
432
handler .removeCallbacks (bluetoothTimeoutRunnable );
445
433
}
446
-
447
434
/**
448
435
* Called when start of the BT SCO channel takes too long time. Usually
449
436
* happens when the BT device has been turned on during an ongoing call.
450
437
*/
451
438
private void bluetoothTimeout () {
439
+ ThreadUtils .checkIsOnMainThread ();
452
440
if (bluetoothState == State .UNINITIALIZED || bluetoothHeadset == null ) {
453
441
return ;
454
442
}
@@ -482,12 +470,10 @@ private void bluetoothTimeout() {
482
470
updateAudioDeviceState ();
483
471
Log .d (TAG , "bluetoothTimeout done: BT state=" + bluetoothState );
484
472
}
485
-
486
473
/** Checks whether audio uses Bluetooth SCO. */
487
474
private boolean isScoOn () {
488
475
return audioManager .isBluetoothScoOn ();
489
476
}
490
-
491
477
/** Converts BluetoothAdapter states into local string representations. */
492
478
private String stateToString (int state ) {
493
479
switch (state ) {
0 commit comments