1 /*
2  * Copyright (C) 2022 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.media.muteawait
18 
19 import android.content.Context
20 import android.graphics.drawable.Drawable
21 import android.media.AudioAttributes.USAGE_MEDIA
22 import android.media.AudioAttributes.USAGE_UNKNOWN
23 import android.media.AudioDeviceAttributes
24 import android.media.AudioDeviceInfo
25 import android.media.AudioManager
26 import android.media.AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION
27 import androidx.test.filters.SmallTest
28 import com.android.settingslib.media.DeviceIconUtil
29 import com.android.settingslib.media.LocalMediaManager
30 import com.android.systemui.R
31 import com.android.systemui.SysuiTestCase
32 import com.android.systemui.util.concurrency.FakeExecutor
33 import com.android.systemui.util.mockito.any
34 import com.android.systemui.util.mockito.eq
35 import com.android.systemui.util.time.FakeSystemClock
36 import org.junit.Before
37 import org.junit.Test
38 import org.mockito.ArgumentCaptor
39 import org.mockito.Mock
40 import org.mockito.Mockito.never
41 import org.mockito.Mockito.reset
42 import org.mockito.Mockito.verify
43 import org.mockito.Mockito.`when` as whenever
44 import org.mockito.MockitoAnnotations
45 
46 
47 @SmallTest
48 class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
49     private lateinit var muteAwaitConnectionManager: MediaMuteAwaitConnectionManager
50     @Mock
51     private lateinit var audioManager: AudioManager
52     @Mock
53     private lateinit var deviceIconUtil: DeviceIconUtil
54     @Mock
55     private lateinit var localMediaManager: LocalMediaManager
56     @Mock
57     private lateinit var logger: MediaMuteAwaitLogger
58     private lateinit var icon: Drawable
59 
60     @Before
61     fun setUp() {
62         MockitoAnnotations.initMocks(this)
63         context.addMockSystemService(Context.AUDIO_SERVICE, audioManager)
64         icon = context.getDrawable(R.drawable.ic_cake)!!
65         whenever(deviceIconUtil.getIconFromAudioDeviceType(any(), any())).thenReturn(icon)
66 
67         muteAwaitConnectionManager = MediaMuteAwaitConnectionManager(
68             FakeExecutor(FakeSystemClock()),
69             localMediaManager,
70             context,
71             deviceIconUtil,
72             logger
73         )
74     }
75 
76     @Test
77     fun constructor_audioManagerCallbackNotRegistered() {
78         verify(audioManager, never()).registerMuteAwaitConnectionCallback(any(), any())
79     }
80 
81     @Test
82     fun startListening_audioManagerCallbackRegistered() {
83         muteAwaitConnectionManager.startListening()
84 
85         verify(audioManager).registerMuteAwaitConnectionCallback(any(), any())
86     }
87 
88     @Test
89     fun stopListening_audioManagerCallbackUnregistered() {
90         muteAwaitConnectionManager.stopListening()
91 
92         verify(audioManager).unregisterMuteAwaitConnectionCallback(any())
93     }
94 
95     @Test
96     fun startListening_audioManagerHasNoMuteAwaitDevice_localMediaMangerNotNotified() {
97         whenever(audioManager.mutingExpectedDevice).thenReturn(null)
98 
99         muteAwaitConnectionManager.startListening()
100 
101         verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
102     }
103 
104     @Test
105     fun startListening_audioManagerHasMuteAwaitDevice_localMediaMangerNotified() {
106         whenever(audioManager.mutingExpectedDevice).thenReturn(DEVICE)
107 
108         muteAwaitConnectionManager.startListening()
109 
110         verify(localMediaManager).dispatchAboutToConnectDeviceAdded(
111             eq(DEVICE_ADDRESS), eq(DEVICE_NAME), eq(icon)
112         )
113     }
114 
115     @Test
116     fun onMutedUntilConnection_notUsageMedia_localMediaManagerNotNotified() {
117         muteAwaitConnectionManager.startListening()
118         val muteAwaitListener = getMuteAwaitListener()
119 
120         muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_UNKNOWN))
121 
122         verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
123     }
124 
125     @Test
126     fun onMutedUntilConnection_isUsageMedia_localMediaManagerNotified() {
127         muteAwaitConnectionManager.startListening()
128         val muteAwaitListener = getMuteAwaitListener()
129 
130 
131         muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
132 
133         verify(localMediaManager).dispatchAboutToConnectDeviceAdded(
134             eq(DEVICE_ADDRESS), eq(DEVICE_NAME), eq(icon)
135         )
136     }
137 
138     @Test
139     fun onUnmutedEvent_noDeviceMutedBefore_localMediaManagerNotNotified() {
140         muteAwaitConnectionManager.startListening()
141         val muteAwaitListener = getMuteAwaitListener()
142 
143         muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA))
144 
145         verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
146     }
147 
148     @Test
149     fun onUnmutedEvent_notSameDevice_localMediaManagerNotNotified() {
150         muteAwaitConnectionManager.startListening()
151         val muteAwaitListener = getMuteAwaitListener()
152         muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
153         reset(localMediaManager)
154 
155         val otherDevice = AudioDeviceAttributes(
156                 AudioDeviceAttributes.ROLE_OUTPUT,
157                 AudioDeviceInfo.TYPE_USB_HEADSET,
158                 "address",
159                 "DifferentName",
160                 listOf(),
161                 listOf(),
162         )
163         muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, otherDevice, intArrayOf(USAGE_MEDIA))
164 
165         verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
166     }
167 
168     @Test
169     fun onUnmutedEvent_notUsageMedia_localMediaManagerNotNotified() {
170         muteAwaitConnectionManager.startListening()
171         val muteAwaitListener = getMuteAwaitListener()
172         muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
173         reset(localMediaManager)
174 
175         muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_UNKNOWN))
176 
177         verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
178     }
179 
180     @Test
181     fun onUnmutedEvent_sameDeviceAndUsageMedia_localMediaManagerNotified() {
182         muteAwaitConnectionManager.startListening()
183         val muteAwaitListener = getMuteAwaitListener()
184         muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
185         reset(localMediaManager)
186 
187         muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA))
188 
189         verify(localMediaManager).dispatchAboutToConnectDeviceRemoved()
190     }
191 
192     @Test
193     fun onMutedUntilConnection_isLogged() {
194         muteAwaitConnectionManager.startListening()
195 
196         getMuteAwaitListener().onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
197 
198         verify(logger).logMutedDeviceAdded(DEVICE_ADDRESS, DEVICE_NAME, hasMediaUsage = true)
199     }
200 
201     @Test
202     fun onUnmutedEvent_notMostRecentDevice_isLogged() {
203         muteAwaitConnectionManager.startListening()
204 
205         getMuteAwaitListener().onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA))
206 
207         verify(logger).logMutedDeviceRemoved(
208             DEVICE_ADDRESS, DEVICE_NAME, hasMediaUsage = true, isMostRecentDevice = false
209         )
210     }
211 
212     @Test
213     fun onUnmutedEvent_isMostRecentDevice_isLogged() {
214         muteAwaitConnectionManager.startListening()
215         val muteAwaitListener = getMuteAwaitListener()
216 
217         muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
218         muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA))
219 
220         verify(logger).logMutedDeviceRemoved(
221             DEVICE_ADDRESS, DEVICE_NAME, hasMediaUsage = true, isMostRecentDevice = true
222         )
223     }
224 
225     private fun getMuteAwaitListener(): AudioManager.MuteAwaitConnectionCallback {
226         val listenerCaptor = ArgumentCaptor.forClass(
227                 AudioManager.MuteAwaitConnectionCallback::class.java
228         )
229         verify(audioManager).registerMuteAwaitConnectionCallback(any(), listenerCaptor.capture())
230         return listenerCaptor.value!!
231     }
232 }
233 
234 private const val DEVICE_ADDRESS = "DeviceAddress"
235 private const val DEVICE_NAME = "DeviceName"
236 private val DEVICE = AudioDeviceAttributes(
237         AudioDeviceAttributes.ROLE_OUTPUT,
238         AudioDeviceInfo.TYPE_USB_HEADSET,
239         DEVICE_ADDRESS,
240         DEVICE_NAME,
241         listOf(),
242         listOf(),
243 )
244