1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15 package com.android.systemui.statusbar.policy.bluetooth
16
17 import android.bluetooth.BluetoothAdapter
18 import android.bluetooth.BluetoothProfile
19 import com.android.settingslib.bluetooth.CachedBluetoothDevice
20 import com.android.settingslib.bluetooth.LocalBluetoothManager
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Application
23 import com.android.systemui.dagger.qualifiers.Background
24 import javax.inject.Inject
25 import kotlinx.coroutines.CoroutineDispatcher
26 import kotlinx.coroutines.CoroutineScope
27 import kotlinx.coroutines.launch
28 import kotlinx.coroutines.withContext
29
30 /**
31 * Repository for information about bluetooth connections.
32 *
33 * Note: Right now, this class and [BluetoothController] co-exist. Any new code should go in this
34 * implementation, but external clients should query [BluetoothController] instead of this class for
35 * now.
36 */
37 interface BluetoothRepository {
38 /**
39 * Fetches the connection statuses for the given [currentDevices] and invokes [callback] once
40 * those statuses have been fetched. The fetching occurs on a background thread because IPCs may
41 * be required to fetch the statuses (see b/271058380).
42 */
fetchConnectionStatusInBackgroundnull43 fun fetchConnectionStatusInBackground(
44 currentDevices: Collection<CachedBluetoothDevice>,
45 callback: ConnectionStatusFetchedCallback,
46 )
47 }
48
49 /** Implementation of [BluetoothRepository]. */
50 @SysUISingleton
51 class BluetoothRepositoryImpl
52 @Inject
53 constructor(
54 @Application private val scope: CoroutineScope,
55 @Background private val bgDispatcher: CoroutineDispatcher,
56 private val localBluetoothManager: LocalBluetoothManager?,
57 ) : BluetoothRepository {
58 override fun fetchConnectionStatusInBackground(
59 currentDevices: Collection<CachedBluetoothDevice>,
60 callback: ConnectionStatusFetchedCallback,
61 ) {
62 scope.launch {
63 val result = fetchConnectionStatus(currentDevices)
64 callback.onConnectionStatusFetched(result)
65 }
66 }
67
68 private suspend fun fetchConnectionStatus(
69 currentDevices: Collection<CachedBluetoothDevice>,
70 ): ConnectionStatusModel {
71 return withContext(bgDispatcher) {
72 val minimumMaxConnectionState =
73 localBluetoothManager?.bluetoothAdapter?.connectionState
74 ?: BluetoothProfile.STATE_DISCONNECTED
75 var maxConnectionState =
76 if (currentDevices.isEmpty()) {
77 minimumMaxConnectionState
78 } else {
79 currentDevices
80 .maxOf { it.maxConnectionState }
81 .coerceAtLeast(minimumMaxConnectionState)
82 }
83
84 val connectedDevices = currentDevices.filter { it.isConnected }
85
86 if (
87 connectedDevices.isEmpty() && maxConnectionState == BluetoothAdapter.STATE_CONNECTED
88 ) {
89 // If somehow we think we are connected, but have no connected devices, we aren't
90 // connected.
91 maxConnectionState = BluetoothAdapter.STATE_DISCONNECTED
92 }
93
94 ConnectionStatusModel(maxConnectionState, connectedDevices)
95 }
96 }
97 }
98
99 data class ConnectionStatusModel(
100 /** The maximum connection state out of all current devices. */
101 val maxConnectionState: Int,
102 /** A list of devices that are currently connected. */
103 val connectedDevices: List<CachedBluetoothDevice>,
104 )
105
106 /** Callback notified when the new status has been fetched. */
interfacenull107 fun interface ConnectionStatusFetchedCallback {
108 fun onConnectionStatusFetched(status: ConnectionStatusModel)
109 }
110