1
1
package com.twilio.audioswitch.bluetooth
2
2
3
- import android.Manifest
4
3
import android.annotation.SuppressLint
5
4
import android.bluetooth.BluetoothAdapter
6
5
import android.bluetooth.BluetoothClass
@@ -16,7 +15,6 @@ import android.content.BroadcastReceiver
16
15
import android.content.Context
17
16
import android.content.Intent
18
17
import android.content.IntentFilter
19
- import android.content.pm.PackageManager.PERMISSION_GRANTED
20
18
import android.media.AudioManager
21
19
import android.media.AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED
22
20
import android.media.AudioManager.SCO_AUDIO_STATE_CONNECTED
@@ -31,7 +29,6 @@ import com.twilio.audioswitch.android.BluetoothDeviceWrapper
31
29
import com.twilio.audioswitch.android.BluetoothIntentProcessor
32
30
import com.twilio.audioswitch.android.BluetoothIntentProcessorImpl
33
31
import com.twilio.audioswitch.android.Logger
34
- import com.twilio.audioswitch.android.PermissionsCheckStrategy
35
32
import com.twilio.audioswitch.android.SystemClockWrapper
36
33
import com.twilio.audioswitch.bluetooth.BluetoothHeadsetManager.HeadsetState.AudioActivated
37
34
import com.twilio.audioswitch.bluetooth.BluetoothHeadsetManager.HeadsetState.AudioActivating
@@ -40,7 +37,6 @@ import com.twilio.audioswitch.bluetooth.BluetoothHeadsetManager.HeadsetState.Con
40
37
import com.twilio.audioswitch.bluetooth.BluetoothHeadsetManager.HeadsetState.Disconnected
41
38
42
39
private const val TAG = " BluetoothHeadsetManager"
43
- private const val PERMISSION_ERROR_MESSAGE = " Bluetooth unsupported, permissions not granted"
44
40
45
41
internal class BluetoothHeadsetManager
46
42
@VisibleForTesting(otherwise = VisibleForTesting .PRIVATE )
@@ -54,7 +50,6 @@ internal constructor(
54
50
systemClockWrapper: SystemClockWrapper = SystemClockWrapper (),
55
51
private val bluetoothIntentProcessor: BluetoothIntentProcessor = BluetoothIntentProcessorImpl (),
56
52
private var headsetProxy: BluetoothHeadset ? = null ,
57
- private val permissionsRequestStrategy: PermissionsCheckStrategy = DefaultPermissionsCheckStrategy (context),
58
53
private var hasRegisteredReceivers: Boolean = false ,
59
54
) : BluetoothProfile .ServiceListener , BroadcastReceiver () {
60
55
@@ -100,6 +95,7 @@ internal constructor(
100
95
}
101
96
}
102
97
98
+ @SuppressLint(" MissingPermission" )
103
99
override fun onServiceConnected (profile : Int , bluetoothProfile : BluetoothProfile ) {
104
100
headsetProxy = bluetoothProfile as BluetoothHeadset
105
101
bluetoothProfile.connectedDevices.forEach { device ->
@@ -200,56 +196,44 @@ internal constructor(
200
196
}
201
197
202
198
fun start (headsetListener : BluetoothHeadsetConnectionListener ) {
203
- if (hasPermissions()) {
204
- this .headsetListener = headsetListener
205
-
206
- bluetoothAdapter.getProfileProxy(
207
- context,
199
+ this .headsetListener = headsetListener
200
+
201
+ bluetoothAdapter.getProfileProxy(
202
+ context,
203
+ this ,
204
+ BluetoothProfile .HEADSET ,
205
+ )
206
+ if (! hasRegisteredReceivers) {
207
+ context.registerReceiver(
208
208
this ,
209
- BluetoothProfile . HEADSET ,
209
+ IntentFilter ( ACTION_CONNECTION_STATE_CHANGED ) ,
210
210
)
211
- if (! hasRegisteredReceivers) {
212
- context.registerReceiver(
213
- this ,
214
- IntentFilter (ACTION_CONNECTION_STATE_CHANGED ),
215
- )
216
- context.registerReceiver(
217
- this ,
218
- IntentFilter (ACTION_AUDIO_STATE_CHANGED ),
219
- )
220
- context.registerReceiver(
221
- this ,
222
- IntentFilter (ACTION_SCO_AUDIO_STATE_UPDATED ),
223
- )
224
- hasRegisteredReceivers = true
225
- }
226
- } else {
227
- logger.w(TAG , PERMISSION_ERROR_MESSAGE )
211
+ context.registerReceiver(
212
+ this ,
213
+ IntentFilter (ACTION_AUDIO_STATE_CHANGED ),
214
+ )
215
+ context.registerReceiver(
216
+ this ,
217
+ IntentFilter (ACTION_SCO_AUDIO_STATE_UPDATED ),
218
+ )
219
+ hasRegisteredReceivers = true
228
220
}
229
221
}
230
222
231
223
fun stop () {
232
- if (hasPermissions()) {
233
- headsetListener = null
234
- bluetoothAdapter.closeProfileProxy(BluetoothProfile .HEADSET , headsetProxy)
235
- if (hasRegisteredReceivers) {
236
- context.unregisterReceiver(this )
237
- hasRegisteredReceivers = false
238
- }
239
- } else {
240
- logger.w(TAG , PERMISSION_ERROR_MESSAGE )
224
+ headsetListener = null
225
+ bluetoothAdapter.closeProfileProxy(BluetoothProfile .HEADSET , headsetProxy)
226
+ if (hasRegisteredReceivers) {
227
+ context.unregisterReceiver(this )
228
+ hasRegisteredReceivers = false
241
229
}
242
230
}
243
231
244
232
fun activate () {
245
- if (hasPermissions()) {
246
- if (headsetState == Connected || headsetState == AudioActivationError ) {
247
- enableBluetoothScoJob.executeBluetoothScoJob()
248
- } else {
249
- logger.w(TAG , " Cannot activate when in the ${headsetState::class .simpleName} state" )
250
- }
233
+ if (headsetState == Connected || headsetState == AudioActivationError ) {
234
+ enableBluetoothScoJob.executeBluetoothScoJob()
251
235
} else {
252
- logger.w(TAG , PERMISSION_ERROR_MESSAGE )
236
+ logger.w(TAG , " Cannot activate when in the ${headsetState:: class .simpleName} state " )
253
237
}
254
238
}
255
239
@@ -262,26 +246,16 @@ internal constructor(
262
246
}
263
247
264
248
fun hasActivationError (): Boolean {
265
- return if (hasPermissions()) {
266
- headsetState == AudioActivationError
267
- } else {
268
- logger.w(TAG , PERMISSION_ERROR_MESSAGE )
269
- false
270
- }
249
+ return headsetState == AudioActivationError
271
250
}
272
251
273
252
// TODO Remove bluetoothHeadsetName param
274
253
fun getHeadset (bluetoothHeadsetName : String? ): AudioDevice .BluetoothHeadset ? {
275
- return if (hasPermissions()) {
276
- if (headsetState != Disconnected ) {
277
- val headsetName = bluetoothHeadsetName ? : getHeadsetName()
278
- headsetName?.let { AudioDevice .BluetoothHeadset (it) }
279
- ? : AudioDevice .BluetoothHeadset ()
280
- } else {
281
- null
282
- }
254
+ return if (headsetState != Disconnected ) {
255
+ val headsetName = bluetoothHeadsetName ? : getHeadsetName()
256
+ headsetName?.let { AudioDevice .BluetoothHeadset (it) }
257
+ ? : AudioDevice .BluetoothHeadset ()
283
258
} else {
284
- logger.w(TAG , PERMISSION_ERROR_MESSAGE )
285
259
null
286
260
}
287
261
}
@@ -309,6 +283,7 @@ internal constructor(
309
283
310
284
private fun hasActiveHeadsetChanged () = headsetState == AudioActivated && hasConnectedDevice() && ! hasActiveHeadset()
311
285
286
+ @SuppressLint(" MissingPermission" )
312
287
private fun getHeadsetName (): String? =
313
288
headsetProxy?.let { proxy ->
314
289
proxy.connectedDevices?.let { devices ->
@@ -331,13 +306,15 @@ internal constructor(
331
306
}
332
307
}
333
308
309
+ @SuppressLint(" MissingPermission" )
334
310
private fun hasActiveHeadset () =
335
311
headsetProxy?.let { proxy ->
336
312
proxy.connectedDevices?.let { devices ->
337
313
devices.any { proxy.isAudioConnected(it) }
338
314
}
339
315
} ? : false
340
316
317
+ @SuppressLint(" MissingPermission" )
341
318
private fun hasConnectedDevice () =
342
319
headsetProxy?.let { proxy ->
343
320
proxy.connectedDevices?.let { devices ->
@@ -359,8 +336,6 @@ internal constructor(
359
336
deviceClass == BluetoothClass .Device .Major .UNCATEGORIZED
360
337
} ? : false
361
338
362
- internal fun hasPermissions () = permissionsRequestStrategy.hasPermissions()
363
-
364
339
@VisibleForTesting(otherwise = VisibleForTesting .PRIVATE )
365
340
internal sealed class HeadsetState {
366
341
object Disconnected : HeadsetState()
@@ -408,27 +383,4 @@ internal constructor(
408
383
headsetState = AudioActivationError
409
384
}
410
385
}
411
-
412
- internal class DefaultPermissionsCheckStrategy (private val context : Context ) :
413
- PermissionsCheckStrategy {
414
- @SuppressLint(" NewApi" )
415
- override fun hasPermissions (): Boolean {
416
- return if (context.applicationInfo.targetSdkVersion <= android.os.Build .VERSION_CODES .R ||
417
- android.os.Build .VERSION .SDK_INT <= android.os.Build .VERSION_CODES .R
418
- ) {
419
- PERMISSION_GRANTED == context.checkPermission(
420
- Manifest .permission.BLUETOOTH ,
421
- android.os.Process .myPid(),
422
- android.os.Process .myUid(),
423
- )
424
- } else {
425
- // for android 12/S or newer
426
- PERMISSION_GRANTED == context.checkPermission(
427
- Manifest .permission.BLUETOOTH_CONNECT ,
428
- android.os.Process .myPid(),
429
- android.os.Process .myUid(),
430
- )
431
- }
432
- }
433
- }
434
386
}
0 commit comments