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