1 /* 2 * Copyright (C) 2016 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 package com.android.car.test; 17 18 import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS; 19 import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_STREAM_STATE; 20 import static com.android.car.test.AudioTestUtils.doRequestFocus; 21 22 import com.google.android.collect.Lists; 23 24 import android.car.Car; 25 import android.car.media.CarAudioManager; 26 import android.content.Context; 27 import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag; 28 import android.hardware.automotive.vehicle.V2_0.VehicleAudioExtFocusFlag; 29 import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusIndex; 30 import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusRequest; 31 import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusState; 32 import android.hardware.automotive.vehicle.V2_0.VehicleAudioStream; 33 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 34 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 35 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; 36 import android.media.AudioAttributes; 37 import android.media.AudioManager; 38 import android.os.SystemClock; 39 import android.test.suitebuilder.annotation.MediumTest; 40 41 import com.android.car.vehiclehal.VehiclePropValueBuilder; 42 import com.android.car.vehiclehal.test.MockedVehicleHal.FailingPropertyHandler; 43 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler; 44 45 import java.util.ArrayList; 46 import java.util.concurrent.Semaphore; 47 import java.util.concurrent.TimeUnit; 48 49 /** 50 * Test to check if system sound can be played without having focus. 51 */ 52 @MediumTest 53 public class CarAudioFocusSystemSoundTest extends MockedCarTestBase { 54 private static final String TAG = CarAudioFocusTest.class.getSimpleName(); 55 56 private static final long TIMEOUT_MS = 3000; 57 58 private final VehicleHalPropertyHandler mAudioRoutingPolicyPropertyHandler = 59 new FailingPropertyHandler() { 60 @Override 61 public void onPropertySet(VehiclePropValue value) { 62 //TODO 63 } 64 }; 65 66 private final FocusPropertyHandler mAudioFocusPropertyHandler = 67 new FocusPropertyHandler(this); 68 69 private AudioManager mAudioManager; 70 71 @Override 72 protected synchronized void configureMockedHal() { 73 addProperty(VehicleProperty.AUDIO_ROUTING_POLICY, mAudioRoutingPolicyPropertyHandler) 74 .setAccess(VehiclePropertyAccess.WRITE); 75 addProperty(VehicleProperty.AUDIO_FOCUS, mAudioFocusPropertyHandler); 76 addProperty(VehicleProperty.AUDIO_STREAM_STATE); 77 78 79 addStaticProperty(VehicleProperty.AUDIO_HW_VARIANT, 80 VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_HW_VARIANT) 81 .addIntValue(-1) 82 .build()) 83 .setConfigArray(Lists.newArrayList(0)); 84 } 85 86 @Override 87 protected void setUp() throws Exception { 88 super.setUp(); 89 // AudioManager should be created in main thread to get focus event. :( 90 runOnMainSync(new Runnable() { 91 @Override 92 public void run() { 93 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 94 } 95 }); 96 } 97 98 private void notifyStreamState(int streamNumber, boolean active) { 99 getMockedVehicleHal().injectEvent(VehiclePropValueBuilder.newBuilder(AUDIO_STREAM_STATE) 100 .setTimestamp() 101 .addIntValue(new int[] { active ? 1 : 0, streamNumber }) 102 .build()); 103 } 104 105 public void testSystemSoundPlayStop() throws Exception { 106 //system sound start 107 notifyStreamState(1, true); 108 int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 109 assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_NO_DUCK, 110 request[0]); 111 assertEquals(0x2, request[1]); 112 assertEquals(0, request[2]); 113 assertEquals(VehicleAudioContextFlag.SYSTEM_SOUND_FLAG, request[3]); 114 mAudioFocusPropertyHandler.sendAudioFocusState( 115 VehicleAudioFocusState.STATE_GAIN_TRANSIENT, 116 request[1], 117 VehicleAudioExtFocusFlag.NONE_FLAG); 118 // system sound stop 119 notifyStreamState(1, false); 120 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 121 assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, 122 request[0]); 123 assertEquals(0, request[1]); 124 assertEquals(0, request[2]); 125 assertEquals(0, request[3]); 126 mAudioFocusPropertyHandler.sendAudioFocusState( 127 VehicleAudioFocusState.STATE_LOSS, 128 request[1], 129 VehicleAudioExtFocusFlag.NONE_FLAG); 130 } 131 132 public void testRadioSystemSound() throws Exception { 133 // radio start 134 AudioFocusListener listenerRadio = new AudioFocusListener(); 135 CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager( 136 Car.AUDIO_SERVICE); 137 assertNotNull(carAudioManager); 138 AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage( 139 CarAudioManager.CAR_AUDIO_USAGE_RADIO); 140 int res = doRequestFocus(mAudioManager, listenerRadio, 141 radioAttributes, AudioManager.AUDIOFOCUS_GAIN); 142 assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res); 143 int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 144 assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]); 145 assertEquals(0, request[1]); 146 assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG, 147 request[2]); 148 assertEquals(VehicleAudioContextFlag.RADIO_FLAG, request[3]); 149 mAudioFocusPropertyHandler.sendAudioFocusState( 150 VehicleAudioFocusState.STATE_GAIN, 151 0, 152 VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG); 153 // system sound start 154 notifyStreamState(1, true); 155 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 156 assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]); 157 assertEquals(0x2, request[1]); 158 assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG, 159 request[2]); 160 assertEquals(VehicleAudioContextFlag.RADIO_FLAG | 161 VehicleAudioContextFlag.SYSTEM_SOUND_FLAG, request[3]); 162 mAudioFocusPropertyHandler.sendAudioFocusState( 163 VehicleAudioFocusState.STATE_GAIN, 164 request[1], 165 VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG); 166 // system sound stop 167 notifyStreamState(1, false); 168 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 169 assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]); 170 assertEquals(0, request[1]); 171 assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG, 172 request[2]); 173 assertEquals(VehicleAudioContextFlag.RADIO_FLAG, request[3]); 174 mAudioFocusPropertyHandler.sendAudioFocusState( 175 VehicleAudioFocusState.STATE_GAIN, 176 0, 177 VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG); 178 // radio stop 179 mAudioManager.abandonAudioFocus(listenerRadio); 180 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 181 assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]); 182 assertEquals(0, request[1]); 183 assertEquals(0, request[2]); 184 assertEquals(0, request[3]); 185 mAudioFocusPropertyHandler.sendAudioFocusState( 186 VehicleAudioFocusState.STATE_LOSS, 187 request[1], 188 VehicleAudioExtFocusFlag.NONE_FLAG); 189 } 190 191 public void testMusicSystemSound() throws Exception { 192 // music start 193 AudioFocusListener listenerMusic = new AudioFocusListener(); 194 int res = doRequestFocus(mAudioManager, listenerMusic, 195 AudioManager.STREAM_MUSIC, 196 AudioManager.AUDIOFOCUS_GAIN); 197 assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res); 198 int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 199 assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]); 200 assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]); 201 assertEquals(0, request[2]); 202 assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]); 203 mAudioFocusPropertyHandler.sendAudioFocusState( 204 VehicleAudioFocusState.STATE_GAIN, 205 request[1], 206 VehicleAudioExtFocusFlag.NONE_FLAG); 207 // system sound start 208 notifyStreamState(1, true); 209 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 210 assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]); 211 assertEquals(0x1 | 0x2, request[1]); 212 assertEquals(0, request[2]); 213 assertEquals(VehicleAudioContextFlag.MUSIC_FLAG | 214 VehicleAudioContextFlag.SYSTEM_SOUND_FLAG, request[3]); 215 mAudioFocusPropertyHandler.sendAudioFocusState( 216 VehicleAudioFocusState.STATE_GAIN, 217 request[1], 218 VehicleAudioExtFocusFlag.NONE_FLAG); 219 // system sound stop 220 notifyStreamState(1, false); 221 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 222 assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]); 223 assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]); 224 assertEquals(0, request[2]); 225 assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]); 226 mAudioFocusPropertyHandler.sendAudioFocusState( 227 VehicleAudioFocusState.STATE_GAIN, 228 request[1], 229 VehicleAudioExtFocusFlag.NONE_FLAG); 230 // music stop 231 mAudioManager.abandonAudioFocus(listenerMusic); 232 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 233 assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]); 234 assertEquals(0, request[1]); 235 assertEquals(0, request[2]); 236 assertEquals(0, request[3]); 237 mAudioFocusPropertyHandler.sendAudioFocusState( 238 VehicleAudioFocusState.STATE_LOSS, 239 request[1], 240 VehicleAudioExtFocusFlag.NONE_FLAG); 241 } 242 243 public void testNavigationSystemSound() throws Exception { 244 // nav guidance start 245 AudioFocusListener listenerNav = new AudioFocusListener(); 246 AudioAttributes navAttrib = (new AudioAttributes.Builder()). 247 setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION). 248 setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE). 249 build(); 250 int res = doRequestFocus(mAudioManager, listenerNav, navAttrib, 251 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); 252 assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res); 253 int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 254 assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK, 255 request[0]); 256 assertEquals(0x2, request[1]); 257 assertEquals(0, request[2]); 258 assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]); 259 mAudioFocusPropertyHandler.sendAudioFocusState( 260 VehicleAudioFocusState.STATE_GAIN_TRANSIENT, 261 request[1], 262 VehicleAudioExtFocusFlag.NONE_FLAG); 263 // system sound start 264 notifyStreamState(1, true); 265 // cannot distinguish this case from nav only. so no focus change. 266 mAudioFocusPropertyHandler.assertNoFocusRequest(1000); 267 // cannot distinguish this case from nav only. so no focus change. 268 notifyStreamState(1, false); 269 mAudioFocusPropertyHandler.assertNoFocusRequest(1000); 270 // nav guidance stop 271 mAudioManager.abandonAudioFocus(listenerNav); 272 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 273 assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]); 274 assertEquals(0, request[1]); 275 assertEquals(0, request[2]); 276 assertEquals(0, request[3]); 277 mAudioFocusPropertyHandler.sendAudioFocusState( 278 VehicleAudioFocusState.STATE_LOSS, 279 request[1], 280 VehicleAudioExtFocusFlag.NONE_FLAG); 281 } 282 283 private static class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener { 284 private final Semaphore mFocusChangeWait = new Semaphore(0); 285 private int mLastFocusChange; 286 287 // TODO: not used? 288 public int waitAndGetFocusChange(long timeoutMs) throws Exception { 289 if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) { 290 fail("timeout waiting for focus change"); 291 } 292 return mLastFocusChange; 293 } 294 295 public void waitForFocus(long timeoutMs, int expectedFocus) throws Exception { 296 while (mLastFocusChange != expectedFocus) { 297 if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) { 298 fail("timeout waiting for focus change"); 299 } 300 } 301 } 302 303 @Override 304 public void onAudioFocusChange(int focusChange) { 305 mLastFocusChange = focusChange; 306 mFocusChangeWait.release(); 307 } 308 } 309 310 private static class FocusPropertyHandler implements VehicleHalPropertyHandler { 311 312 private int mState = VehicleAudioFocusState.STATE_LOSS; 313 private int mStreams = 0; 314 private int mExtFocus = 0; 315 private int mRequest; 316 private int mRequestedStreams; 317 private int mRequestedExtFocus; 318 private int mRequestedAudioContexts; 319 private final MockedCarTestBase mCarTest; 320 321 private final Semaphore mSetWaitSemaphore = new Semaphore(0); 322 323 FocusPropertyHandler(MockedCarTestBase carTest) { 324 mCarTest = carTest; 325 } 326 327 void sendAudioFocusState(int state, int streams, int extFocus) { 328 synchronized (this) { 329 mState = state; 330 mStreams = streams; 331 mExtFocus = extFocus; 332 } 333 mCarTest.getMockedVehicleHal().injectEvent( 334 VehiclePropValueBuilder.newBuilder(AUDIO_FOCUS) 335 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 336 .addIntValue(state, streams, extFocus, 0) 337 .build()); 338 } 339 340 int[] waitForAudioFocusRequest(long timeoutMs) throws Exception { 341 if (!mSetWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) { 342 fail("timeout"); 343 } 344 synchronized (this) { 345 return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus, 346 mRequestedAudioContexts }; 347 } 348 } 349 350 void assertNoFocusRequest(long timeoutMs) throws Exception { 351 if (mSetWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) { 352 fail("should not get focus request"); 353 } 354 } 355 356 @Override 357 public void onPropertySet(VehiclePropValue value) { 358 assertEquals(AUDIO_FOCUS, value.prop); 359 ArrayList<Integer> v = value.value.int32Values; 360 synchronized (this) { 361 mRequest = v.get(VehicleAudioFocusIndex.FOCUS); 362 mRequestedStreams = v.get(VehicleAudioFocusIndex.STREAMS); 363 mRequestedExtFocus = v.get(VehicleAudioFocusIndex.EXTERNAL_FOCUS_STATE); 364 mRequestedAudioContexts = v.get(VehicleAudioFocusIndex.AUDIO_CONTEXTS); 365 } 366 mSetWaitSemaphore.release(); 367 } 368 369 @Override 370 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 371 assertEquals(VehicleProperty.AUDIO_FOCUS, value.prop); 372 int state, streams, extFocus; 373 synchronized (this) { 374 state = mState; 375 streams = mStreams; 376 extFocus = mExtFocus; 377 } 378 return VehiclePropValueBuilder.newBuilder(AUDIO_FOCUS) 379 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 380 .addIntValue(state, streams, extFocus, 0) 381 .build(); 382 } 383 384 @Override 385 public void onPropertySubscribe(int property, int zones, float sampleRate) { 386 assertEquals(VehicleProperty.AUDIO_FOCUS, property); 387 } 388 389 @Override 390 public void onPropertyUnsubscribe(int property) { 391 assertEquals(VehicleProperty.AUDIO_FOCUS, property); 392 } 393 } 394 } 395