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