1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.bluetooth.qsdialog
18 
19 import android.bluetooth.BluetoothDevice
20 import android.content.pm.ApplicationInfo
21 import android.content.pm.PackageManager
22 import android.media.AudioManager
23 import android.platform.test.annotations.DisableFlags
24 import android.platform.test.annotations.EnableFlags
25 import android.testing.TestableLooper
26 import androidx.test.ext.junit.runners.AndroidJUnit4
27 import androidx.test.filters.SmallTest
28 import com.android.settingslib.bluetooth.CachedBluetoothDevice
29 import com.android.settingslib.flags.Flags
30 import com.android.systemui.SysuiTestCase
31 import com.google.common.truth.Truth.assertThat
32 import org.junit.Before
33 import org.junit.Rule
34 import org.junit.Test
35 import org.junit.runner.RunWith
36 import org.mockito.Mock
37 import org.mockito.Mockito.`when`
38 import org.mockito.junit.MockitoJUnit
39 import org.mockito.junit.MockitoRule
40 
41 @SmallTest
42 @RunWith(AndroidJUnit4::class)
43 @TestableLooper.RunWithLooper(setAsMainLooper = true)
44 class DeviceItemFactoryTest : SysuiTestCase() {
45     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
46 
47     @Mock private lateinit var cachedDevice: CachedBluetoothDevice
48     @Mock private lateinit var bluetoothDevice: BluetoothDevice
49     @Mock private lateinit var packageManager: PackageManager
50 
51     private val availableMediaDeviceItemFactory = AvailableMediaDeviceItemFactory()
52     private val connectedDeviceItemFactory = ConnectedDeviceItemFactory()
53     private val savedDeviceItemFactory = SavedDeviceItemFactory()
54 
55     private val audioManager = context.getSystemService(AudioManager::class.java)!!
56 
57     @Before
setupnull58     fun setup() {
59         `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
60         `when`(cachedDevice.address).thenReturn(DEVICE_ADDRESS)
61         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
62         `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
63 
64         context.setMockPackageManager(packageManager)
65     }
66 
67     @Test
testAvailableMediaDeviceItemFactory_createFromCachedDevicenull68     fun testAvailableMediaDeviceItemFactory_createFromCachedDevice() {
69         val deviceItem = availableMediaDeviceItemFactory.create(context, cachedDevice)
70 
71         assertDeviceItem(deviceItem, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
72     }
73 
74     @Test
testConnectedDeviceItemFactory_createFromCachedDevicenull75     fun testConnectedDeviceItemFactory_createFromCachedDevice() {
76         val deviceItem = connectedDeviceItemFactory.create(context, cachedDevice)
77 
78         assertDeviceItem(deviceItem, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
79     }
80 
81     @Test
testSavedDeviceItemFactory_createFromCachedDevicenull82     fun testSavedDeviceItemFactory_createFromCachedDevice() {
83         val deviceItem = savedDeviceItemFactory.create(context, cachedDevice)
84 
85         assertDeviceItem(deviceItem, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
86         assertThat(deviceItem.background).isNotNull()
87     }
88 
89     @Test
90     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTruenull91     fun testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTrue() {
92         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
93         `when`(cachedDevice.isConnected).thenReturn(false)
94 
95         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
96             .isTrue()
97     }
98 
99     @Test
100     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_connected_returnsFalsenull101     fun testSavedFactory_isFilterMatched_connected_returnsFalse() {
102         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
103         `when`(cachedDevice.isConnected).thenReturn(true)
104 
105         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
106             .isFalse()
107     }
108 
109     @Test
110     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_notBonded_returnsFalsenull111     fun testSavedFactory_isFilterMatched_notBonded_returnsFalse() {
112         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
113         `when`(cachedDevice.isConnected).thenReturn(false)
114 
115         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
116             .isFalse()
117     }
118 
119     @Test
120     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalsenull121     fun testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
122         `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
123             .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
124         `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
125             .thenReturn(ApplicationInfo())
126         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
127         `when`(cachedDevice.isConnected).thenReturn(false)
128 
129         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
130             .isFalse()
131     }
132 
133     @Test
134     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_noExclusiveManager_returnsTruenull135     fun testSavedFactory_isFilterMatched_noExclusiveManager_returnsTrue() {
136         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
137         `when`(cachedDevice.isConnected).thenReturn(false)
138 
139         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
140             .isTrue()
141     }
142 
143     @Test
144     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_exclusiveManagerNotEnabled_returnsTruenull145     fun testSavedFactory_isFilterMatched_exclusiveManagerNotEnabled_returnsTrue() {
146         `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
147             .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
148         `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
149             .thenReturn(ApplicationInfo().also { it.enabled = false })
150         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
151         `when`(cachedDevice.isConnected).thenReturn(false)
152 
153         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
154             .isTrue()
155     }
156 
157     @Test
158     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_exclusiveManagerNotInstalled_returnsTruenull159     fun testSavedFactory_isFilterMatched_exclusiveManagerNotInstalled_returnsTrue() {
160         `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
161             .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
162         `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
163             .thenThrow(PackageManager.NameNotFoundException("Test!"))
164         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
165         `when`(cachedDevice.isConnected).thenReturn(false)
166 
167         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
168             .isTrue()
169     }
170 
171     @Test
172     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalsenull173     fun testSavedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalse() {
174         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
175         `when`(cachedDevice.isConnected).thenReturn(false)
176 
177         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
178             .isFalse()
179     }
180 
181     @Test
182     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalsenull183     fun testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalse() {
184         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
185         `when`(cachedDevice.isConnected).thenReturn(true)
186 
187         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
188             .isFalse()
189     }
190 
191     @Test
192     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTruenull193     fun testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTrue() {
194         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
195         `when`(bluetoothDevice.isConnected).thenReturn(true)
196         audioManager.setMode(AudioManager.MODE_NORMAL)
197 
198         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
199             .isTrue()
200     }
201 
202     @Test
203     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_notConnected_returnsFalsenull204     fun testConnectedFactory_isFilterMatched_notConnected_returnsFalse() {
205         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
206         `when`(bluetoothDevice.isConnected).thenReturn(false)
207         audioManager.setMode(AudioManager.MODE_NORMAL)
208 
209         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
210             .isFalse()
211     }
212 
213     @Test
214     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_notBonded_returnsFalsenull215     fun testConnectedFactory_isFilterMatched_notBonded_returnsFalse() {
216         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
217         `when`(bluetoothDevice.isConnected).thenReturn(true)
218         audioManager.setMode(AudioManager.MODE_NORMAL)
219 
220         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
221             .isFalse()
222     }
223 
224     @Test
225     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalsenull226     fun testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
227         `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
228             .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
229         `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
230             .thenReturn(ApplicationInfo())
231         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
232         `when`(bluetoothDevice.isConnected).thenReturn(true)
233         audioManager.setMode(AudioManager.MODE_NORMAL)
234 
235         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
236             .isFalse()
237     }
238 
239     @Test
240     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTruenull241     fun testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTrue() {
242         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
243         `when`(bluetoothDevice.isConnected).thenReturn(true)
244         audioManager.setMode(AudioManager.MODE_NORMAL)
245 
246         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
247             .isTrue()
248     }
249 
250     @Test
251     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_exclusiveManagerNotEnabled_returnsTruenull252     fun testConnectedFactory_isFilterMatched_exclusiveManagerNotEnabled_returnsTrue() {
253         `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
254             .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
255         `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
256             .thenReturn(ApplicationInfo().also { it.enabled = false })
257         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
258         `when`(bluetoothDevice.isConnected).thenReturn(true)
259         audioManager.setMode(AudioManager.MODE_NORMAL)
260 
261         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
262             .isTrue()
263     }
264 
265     @Test
266     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_exclusiveManagerNotInstalled_returnsTruenull267     fun testConnectedFactory_isFilterMatched_exclusiveManagerNotInstalled_returnsTrue() {
268         `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
269             .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
270         `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
271             .thenThrow(PackageManager.NameNotFoundException("Test!"))
272         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
273         `when`(bluetoothDevice.isConnected).thenReturn(true)
274         audioManager.setMode(AudioManager.MODE_NORMAL)
275 
276         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
277             .isTrue()
278     }
279 
280     @Test
281     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalsenull282     fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalse() {
283         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
284         `when`(bluetoothDevice.isConnected).thenReturn(true)
285         audioManager.setMode(AudioManager.MODE_NORMAL)
286 
287         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
288             .isFalse()
289     }
290 
291     @Test
292     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalsenull293     fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalse() {
294         `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
295         `when`(bluetoothDevice.isConnected).thenReturn(false)
296         audioManager.setMode(AudioManager.MODE_NORMAL)
297 
298         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
299             .isFalse()
300     }
301 
assertDeviceItemnull302     private fun assertDeviceItem(deviceItem: DeviceItem?, deviceItemType: DeviceItemType) {
303         assertThat(deviceItem).isNotNull()
304         assertThat(deviceItem!!.type).isEqualTo(deviceItemType)
305         assertThat(deviceItem.cachedBluetoothDevice).isEqualTo(cachedDevice)
306         assertThat(deviceItem.deviceName).isEqualTo(DEVICE_NAME)
307         assertThat(deviceItem.connectionSummary).isEqualTo(CONNECTION_SUMMARY)
308     }
309 
310     companion object {
311         const val DEVICE_NAME = "DeviceName"
312         const val CONNECTION_SUMMARY = "ConnectionSummary"
313         private const val TEST_EXCLUSIVE_MANAGER = "com.test.manager"
314         private const val DEVICE_ADDRESS = "04:52:C7:0B:D8:3C"
315     }
316 }
317