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.*; 20 21 import android.content.Context; 22 import android.content.pm.PackageManager; 23 import android.content.res.Resources; 24 import android.media.AudioManager; 25 import android.os.HandlerThread; 26 import android.os.Looper; 27 import android.support.test.InstrumentationRegistry; 28 import android.support.test.filters.MediumTest; 29 import android.support.test.runner.AndroidJUnit4; 30 31 import com.android.bluetooth.R; 32 33 import org.junit.Assume; 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 import org.mockito.Mock; 38 import org.mockito.MockitoAnnotations; 39 40 @MediumTest 41 @RunWith(AndroidJUnit4.class) 42 public class A2dpSinkStreamHandlerTest { 43 private static final int DUCK_PERCENT = 75; 44 private HandlerThread mHandlerThread; 45 private A2dpSinkStreamHandler mStreamHandler; 46 private Context mTargetContext; 47 48 @Mock private Context mMockContext; 49 50 @Mock private A2dpSinkStateMachine mMockA2dpSink; 51 52 @Mock private AudioManager mMockAudioManager; 53 54 @Mock private Resources mMockResources; 55 56 @Mock private PackageManager mMockPackageManager; 57 58 @Before 59 public void setUp() { 60 mTargetContext = InstrumentationRegistry.getTargetContext(); 61 Assume.assumeTrue("Ignore test when A2dpSinkService is not enabled", 62 mTargetContext.getResources().getBoolean(R.bool.profile_supported_a2dp_sink)); 63 MockitoAnnotations.initMocks(this); 64 // Mock the looper 65 if (Looper.myLooper() == null) { 66 Looper.prepare(); 67 } 68 69 mHandlerThread = new HandlerThread("A2dpSinkStreamHandlerTest"); 70 mHandlerThread.start(); 71 72 when(mMockContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mMockAudioManager); 73 when(mMockContext.getResources()).thenReturn(mMockResources); 74 when(mMockResources.getInteger(anyInt())).thenReturn(DUCK_PERCENT); 75 when(mMockAudioManager.requestAudioFocus(any())).thenReturn( 76 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 77 when(mMockAudioManager.abandonAudioFocus(any())).thenReturn(AudioManager.AUDIOFOCUS_GAIN); 78 doNothing().when(mMockA2dpSink).informAudioTrackGainNative(anyFloat()); 79 when(mMockContext.getMainLooper()).thenReturn(mHandlerThread.getLooper()); 80 when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); 81 when(mMockPackageManager.hasSystemFeature(any())).thenReturn(false); 82 83 mStreamHandler = spy(new A2dpSinkStreamHandler(mMockA2dpSink, mMockContext)); 84 } 85 86 @Test 87 public void testSrcStart() { 88 // Stream started without local play, expect no change in streaming. 89 mStreamHandler.handleMessage( 90 mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_STR_START)); 91 verify(mMockAudioManager, times(0)).requestAudioFocus(any()); 92 verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(1); 93 verify(mMockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 94 } 95 96 @Test 97 public void testSrcStop() { 98 // Stream stopped without local play, expect no change in streaming. 99 mStreamHandler.handleMessage( 100 mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_STR_STOP)); 101 verify(mMockAudioManager, times(0)).requestAudioFocus(any()); 102 verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(1); 103 verify(mMockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 104 } 105 106 @Test 107 public void testSnkPlay() { 108 // Play was pressed locally, expect streaming to start. 109 mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SNK_PLAY)); 110 verify(mMockAudioManager, times(1)).requestAudioFocus(any()); 111 verify(mMockA2dpSink, times(1)).informAudioFocusStateNative(1); 112 verify(mMockA2dpSink, times(1)).informAudioTrackGainNative(1.0f); 113 } 114 115 @Test 116 public void testSnkPause() { 117 // Pause was pressed locally, expect streaming to stop. 118 mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SNK_PAUSE)); 119 verify(mMockAudioManager, times(0)).requestAudioFocus(any()); 120 verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(1); 121 verify(mMockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 122 } 123 124 @Test 125 public void testDisconnect() { 126 // Remote device was disconnected, expect streaming to stop. 127 testSnkPlay(); 128 mStreamHandler.handleMessage( 129 mStreamHandler.obtainMessage(A2dpSinkStreamHandler.DISCONNECT)); 130 verify(mMockAudioManager, times(0)).abandonAudioFocus(any()); 131 verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(0); 132 } 133 134 @Test 135 public void testSrcPlay() { 136 // Play was pressed remotely, expect no streaming due to lack of audio focus. 137 mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY)); 138 verify(mMockAudioManager, times(0)).requestAudioFocus(any()); 139 verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(1); 140 verify(mMockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 141 } 142 143 @Test 144 public void testSrcPlayIot() { 145 // Play was pressed remotely for an iot device, expect streaming to start. 146 when(mMockPackageManager.hasSystemFeature(any())).thenReturn(true); 147 mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY)); 148 verify(mMockAudioManager, times(1)).requestAudioFocus(any()); 149 verify(mMockA2dpSink, times(1)).informAudioFocusStateNative(1); 150 verify(mMockA2dpSink, times(1)).informAudioTrackGainNative(1.0f); 151 } 152 153 @Test 154 public void testSrcPause() { 155 // Play was pressed locally, expect streaming to start. 156 mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY)); 157 verify(mMockAudioManager, times(0)).requestAudioFocus(any()); 158 verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(1); 159 verify(mMockA2dpSink, times(0)).informAudioTrackGainNative(1.0f); 160 } 161 162 @Test 163 public void testFocusGain() { 164 // Focus was gained, expect streaming to resume. 165 testSnkPlay(); 166 mStreamHandler.handleMessage( 167 mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE, 168 AudioManager.AUDIOFOCUS_GAIN)); 169 verify(mMockAudioManager, times(1)).requestAudioFocus(any()); 170 verify(mMockA2dpSink, times(2)).informAudioFocusStateNative(1); 171 verify(mMockA2dpSink, times(2)).informAudioTrackGainNative(1.0f); 172 } 173 174 @Test 175 public void testFocusTransientMayDuck() { 176 // TransientMayDuck focus was gained, expect audio stream to duck. 177 testSnkPlay(); 178 mStreamHandler.handleMessage( 179 mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE, 180 AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK)); 181 verify(mMockA2dpSink, times(1)).informAudioTrackGainNative(DUCK_PERCENT / 100.0f); 182 } 183 184 @Test 185 public void testFocusLostTransient() { 186 // Focus was lost transiently, expect streaming to stop. 187 testSnkPlay(); 188 mStreamHandler.handleMessage( 189 mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE, 190 AudioManager.AUDIOFOCUS_LOSS_TRANSIENT)); 191 verify(mMockAudioManager, times(0)).abandonAudioFocus(any()); 192 verify(mMockA2dpSink, times(1)).informAudioFocusStateNative(0); 193 } 194 195 @Test 196 public void testFocusLost() { 197 // Focus was lost permanently, expect streaming to stop. 198 testSnkPlay(); 199 mStreamHandler.handleMessage( 200 mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE, 201 AudioManager.AUDIOFOCUS_LOSS)); 202 verify(mMockAudioManager, times(1)).abandonAudioFocus(any()); 203 verify(mMockA2dpSink, times(1)).informAudioFocusStateNative(0); 204 } 205 } 206