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