1 /* 2 * Copyright (C) 2017 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.bluetooth.a2dpsink; 18 19 import static org.mockito.Mockito.any; 20 import static org.mockito.Mockito.anyFloat; 21 import static org.mockito.Mockito.anyInt; 22 import static org.mockito.Mockito.doNothing; 23 import static org.mockito.Mockito.eq; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.content.Context; 30 import android.content.res.Resources; 31 import android.media.AudioManager; 32 import android.media.AudioManager.OnAudioFocusChangeListener; 33 import android.os.HandlerThread; 34 import android.os.Looper; 35 import android.test.AndroidTestCase; 36 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.runners.MockitoJUnitRunner; 43 44 @RunWith(MockitoJUnitRunner.class) 45 public class A2dpSinkStreamHandlerTest extends AndroidTestCase { 46 static final int DUCK_PERCENT = 75; 47 private HandlerThread mHandlerThread; 48 A2dpSinkStreamHandler streamHandler; 49 ArgumentCaptor<OnAudioFocusChangeListener> audioFocusChangeListenerArgumentCaptor; 50 51 @Mock Context mockContext; 52 53 @Mock A2dpSinkStateMachine mockA2dpSink; 54 55 @Mock AudioManager mockAudioManager; 56 57 @Mock Resources mockResources; 58 59 @Before 60 public void setUp() { 61 // Mock the looper 62 if (Looper.myLooper() == null) { 63 Looper.prepare(); 64 } 65 66 mHandlerThread = new HandlerThread("A2dpSinkStreamHandlerTest"); 67 mHandlerThread.start(); 68 69 audioFocusChangeListenerArgumentCaptor = 70 ArgumentCaptor.forClass(OnAudioFocusChangeListener.class); 71 when(mockContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mockAudioManager); 72 when(mockContext.getResources()).thenReturn(mockResources); 73 when(mockResources.getInteger(anyInt())).thenReturn(DUCK_PERCENT); 74 when(mockAudioManager.requestAudioFocus(audioFocusChangeListenerArgumentCaptor.capture(), 75 eq(AudioManager.STREAM_MUSIC), eq(AudioManager.AUDIOFOCUS_GAIN))) 76 .thenReturn(AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 77 when(mockAudioManager.abandonAudioFocus(any())).thenReturn(AudioManager.AUDIOFOCUS_GAIN); 78 doNothing().when(mockA2dpSink).informAudioTrackGainNative(anyFloat()); 79 when(mockContext.getMainLooper()).thenReturn(mHandlerThread.getLooper()); 80 81 streamHandler = spy(new A2dpSinkStreamHandler(mockA2dpSink, mockContext)); 82 } 83 84 @Test 85 public void testSrcStart() { 86 // Stream started without local play, expect no change in streaming. 87 streamHandler.handleMessage( 88 streamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_STR_START)); 89 verify(mockAudioManager, times(0)).requestAudioFocus(any(), anyInt(), anyInt()); 90 verify(mockA2dpSink, times(0)).informAudioFocusStateNative(1); 91 verify(mockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 92 } 93 94 @Test 95 public void testSrcStop() { 96 // Stream stopped without local play, expect no change in streaming. 97 streamHandler.handleMessage( 98 streamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_STR_STOP)); 99 verify(mockAudioManager, times(0)).requestAudioFocus(any(), anyInt(), anyInt()); 100 verify(mockA2dpSink, times(0)).informAudioFocusStateNative(1); 101 verify(mockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 102 } 103 104 @Test 105 public void testSnkPlay() { 106 // Play was pressed locally, expect streaming to start. 107 streamHandler.handleMessage(streamHandler.obtainMessage(A2dpSinkStreamHandler.SNK_PLAY)); 108 verify(mockAudioManager, times(1)).requestAudioFocus(any(), anyInt(), anyInt()); 109 verify(mockA2dpSink, times(1)).informAudioFocusStateNative(1); 110 verify(mockA2dpSink, times(1)).informAudioTrackGainNative(1.0f); 111 } 112 113 @Test 114 public void testSnkPause() { 115 // Pause was pressed locally, expect streaming to stop. 116 streamHandler.handleMessage(streamHandler.obtainMessage(A2dpSinkStreamHandler.SNK_PAUSE)); 117 verify(mockAudioManager, times(0)).requestAudioFocus(any(), anyInt(), anyInt()); 118 verify(mockA2dpSink, times(0)).informAudioFocusStateNative(1); 119 verify(mockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 120 } 121 122 @Test 123 public void testDisconnect() { 124 // Remote device was disconnected, expect streaming to stop. 125 testSnkPlay(); 126 streamHandler.handleMessage(streamHandler.obtainMessage(A2dpSinkStreamHandler.DISCONNECT)); 127 verify(mockAudioManager, times(1)).abandonAudioFocus(any()); 128 verify(mockA2dpSink, times(1)).informAudioFocusStateNative(0); 129 } 130 131 @Test 132 public void testSrcPlay() { 133 // Play was pressed remotely, expect no streaming due to lack of audio focus. 134 streamHandler.handleMessage(streamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY)); 135 verify(mockAudioManager, times(0)).requestAudioFocus(any(), anyInt(), anyInt()); 136 verify(mockA2dpSink, times(0)).informAudioFocusStateNative(1); 137 verify(mockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 138 } 139 140 @Test 141 public void testSrcPause() { 142 // Play was pressed locally, expect streaming to start. 143 streamHandler.handleMessage(streamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY)); 144 verify(mockAudioManager, times(0)).requestAudioFocus(any(), anyInt(), anyInt()); 145 verify(mockA2dpSink, times(0)).informAudioFocusStateNative(1); 146 verify(mockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 147 } 148 149 @Test 150 public void testFocusGain() { 151 // Focus was gained, expect streaming to resume. 152 testSnkPlay(); 153 streamHandler.handleMessage(streamHandler.obtainMessage( 154 A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE, AudioManager.AUDIOFOCUS_GAIN)); 155 verify(mockAudioManager, times(1)).requestAudioFocus(any(), anyInt(), anyInt()); 156 verify(mockA2dpSink, times(2)).informAudioFocusStateNative(1); 157 verify(mockA2dpSink, times(2)).informAudioTrackGainNative(1.0f); 158 } 159 160 @Test 161 public void testFocusTransientMayDuck() { 162 // TransientMayDuck focus was gained, expect audio stream to duck. 163 testSnkPlay(); 164 streamHandler.handleMessage( 165 streamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE, 166 AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK)); 167 verify(mockA2dpSink, times(1)).informAudioTrackGainNative(DUCK_PERCENT / 100.0f); 168 } 169 170 @Test 171 public void testFocusLostTransient() { 172 // Focus was lost transiently, expect streaming to stop. 173 testSnkPlay(); 174 streamHandler.handleMessage(streamHandler.obtainMessage( 175 A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT)); 176 verify(mockAudioManager, times(0)).abandonAudioFocus(any()); 177 verify(mockA2dpSink, times(1)).informAudioFocusStateNative(0); 178 } 179 180 @Test 181 public void testFocusLost() { 182 // Focus was lost permanently, expect streaming to stop. 183 testSnkPlay(); 184 streamHandler.handleMessage(streamHandler.obtainMessage( 185 A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE, AudioManager.AUDIOFOCUS_LOSS)); 186 verify(mockAudioManager, times(1)).abandonAudioFocus(any()); 187 verify(mockA2dpSink, times(1)).informAudioFocusStateNative(0); 188 } 189 } 190