CarVolumeServiceTest.java revision 9f6f2546729b204c69195e0908a8ea750e7ce9dd
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 */ 16package com.android.car.test; 17 18import static com.android.car.test.AudioTestUtils.doRequestFocus; 19 20import com.google.android.collect.Lists; 21 22import android.car.Car; 23import android.car.CarNotConnectedException; 24import android.car.media.CarAudioManager; 25import android.content.Context; 26import android.hardware.automotive.vehicle.V2_0.VehicleAudioExtFocusFlag; 27import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusState; 28import android.hardware.automotive.vehicle.V2_0.VehicleAudioVolumeIndex; 29import android.hardware.automotive.vehicle.V2_0.VehicleHwKeyInputAction; 30import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 31import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 32import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; 33import android.media.AudioAttributes; 34import android.media.AudioManager; 35import android.media.IVolumeController; 36import android.os.RemoteException; 37import android.os.SystemClock; 38import android.test.suitebuilder.annotation.MediumTest; 39import android.util.Log; 40import android.util.Pair; 41import android.util.SparseIntArray; 42import android.view.KeyEvent; 43 44import com.android.car.vehiclehal.VehiclePropValueBuilder; 45import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler; 46import com.android.internal.annotations.GuardedBy; 47 48import java.util.ArrayList; 49import java.util.Arrays; 50import java.util.List; 51 52@MediumTest 53public class CarVolumeServiceTest extends MockedCarTestBase { 54 private static final String TAG = CarVolumeServiceTest.class.getSimpleName(); 55 56 private static final int MIN_VOL = 1; 57 private static final int MAX_VOL = 20; 58 private static final long TIMEOUT_MS = 3000; 59 private static final long POLL_INTERVAL_MS = 50; 60 61 private static final int[] LOGICAL_STREAMS = { 62 AudioManager.STREAM_VOICE_CALL, 63 AudioManager.STREAM_SYSTEM, 64 AudioManager.STREAM_RING, 65 AudioManager.STREAM_MUSIC, 66 AudioManager.STREAM_ALARM, 67 AudioManager.STREAM_NOTIFICATION, 68 AudioManager.STREAM_DTMF, 69 }; 70 71 private CarAudioManager mCarAudioManager; 72 private AudioManager mAudioManager; 73 74 @Override 75 protected synchronized void setUp() throws Exception { 76 super.setUp(); 77 // AudioManager should be created in main thread to get focus event. :( 78 runOnMainSync(new Runnable() { 79 @Override 80 public void run() { 81 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 82 } 83 }); 84 85 List<Integer> maxs = new ArrayList<>(); 86 maxs.add(MAX_VOL); 87 maxs.add(MAX_VOL); 88 89 // TODO: add tests for audio context supported cases. 90 startVolumeEmulation(0 /*supported audio context*/, maxs); 91 mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE); 92 } 93 94 public void testVolumeLimits() throws Exception { 95 for (int stream : LOGICAL_STREAMS) { 96 assertEquals(MAX_VOL, mCarAudioManager.getStreamMaxVolume(stream)); 97 } 98 } 99 100 public void testVolumeSet() { 101 try { 102 int callVol = 10; 103 int musicVol = 15; 104 mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0); 105 mCarAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, callVol, 0); 106 107 volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, musicVol), 108 createStreamVolPair(AudioManager.STREAM_VOICE_CALL, callVol)); 109 110 musicVol = MAX_VOL + 1; 111 mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0); 112 113 volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, MAX_VOL), 114 createStreamVolPair(AudioManager.STREAM_VOICE_CALL, callVol)); 115 } catch (CarNotConnectedException e) { 116 fail("Car not connected"); 117 } 118 } 119 120 public void testSuppressVolumeUI() { 121 try { 122 VolumeController volumeController = new VolumeController(); 123 mCarAudioManager.setVolumeController(volumeController); 124 125 // first give focus to system sound 126 CarAudioFocusTest.AudioFocusListener listenerMusic = 127 new CarAudioFocusTest.AudioFocusListener(); 128 int res = doRequestFocus(mAudioManager, listenerMusic, 129 AudioManager.STREAM_SYSTEM, 130 AudioManager.AUDIOFOCUS_GAIN); 131 assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res); 132 int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 133 mAudioFocusPropertyHandler.sendAudioFocusState( 134 VehicleAudioFocusState.STATE_GAIN, 135 request[1], 136 VehicleAudioExtFocusFlag.NONE_FLAG); 137 138 // focus gives to Alarm, there should be a audio context change. 139 CarAudioFocusTest.AudioFocusListener listenerAlarm = new 140 CarAudioFocusTest.AudioFocusListener(); 141 AudioAttributes callAttrib = (new AudioAttributes.Builder()). 142 setUsage(AudioAttributes.USAGE_ALARM). 143 build(); 144 res = doRequestFocus(mAudioManager, listenerAlarm, callAttrib, 145 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); 146 assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res); 147 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 148 mAudioFocusPropertyHandler.sendAudioFocusState( 149 VehicleAudioFocusState.STATE_GAIN, request[1], 150 VehicleAudioExtFocusFlag.NONE_FLAG); 151 // should not show UI 152 volumeChangeVerificationPoll(AudioManager.STREAM_ALARM, false, volumeController); 153 154 int alarmVol = mCarAudioManager.getStreamVolume(AudioManager.STREAM_ALARM); 155 // set alarm volume with show_ui flag and a different volume 156 mCarAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 157 (alarmVol + 1) % mCarAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 158 AudioManager.FLAG_SHOW_UI); 159 // should show ui 160 volumeChangeVerificationPoll(AudioManager.STREAM_ALARM, true, volumeController); 161 mAudioManager.abandonAudioFocus(listenerAlarm); 162 } catch (Exception e) { 163 fail(e.getMessage()); 164 } 165 } 166 167 public void testVolumeKeys() throws Exception { 168 try { 169 int musicVol = 10; 170 mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0); 171 int callVol = 12; 172 mCarAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, callVol, 0); 173 174 CarAudioFocusTest.AudioFocusListener listenerMusic = 175 new CarAudioFocusTest.AudioFocusListener(); 176 int res = doRequestFocus(mAudioManager, listenerMusic, 177 AudioManager.STREAM_MUSIC, 178 AudioManager.AUDIOFOCUS_GAIN); 179 assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res); 180 int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 181 mAudioFocusPropertyHandler.sendAudioFocusState( 182 VehicleAudioFocusState.STATE_GAIN, 183 request[1], 184 VehicleAudioExtFocusFlag.NONE_FLAG); 185 186 187 assertEquals(musicVol, mCarAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)); 188 sendVolumeKey(true /*vol up*/); 189 musicVol++; 190 volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, musicVol)); 191 192 // call start 193 CarAudioFocusTest.AudioFocusListener listenerCall = new 194 CarAudioFocusTest.AudioFocusListener(); 195 AudioAttributes callAttrib = (new AudioAttributes.Builder()). 196 setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION). 197 build(); 198 doRequestFocus(mAudioManager, listenerCall, callAttrib, 199 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); 200 request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS); 201 mAudioFocusPropertyHandler.sendAudioFocusState( 202 VehicleAudioFocusState.STATE_GAIN, request[1], 203 VehicleAudioExtFocusFlag.NONE_FLAG); 204 205 sendVolumeKey(true /*vol up*/); 206 callVol++; 207 volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, musicVol), 208 createStreamVolPair(AudioManager.STREAM_VOICE_CALL, callVol)); 209 } catch (CarNotConnectedException | InterruptedException e) { 210 fail(e.toString()); 211 } 212 } 213 214 private Pair<Integer, Integer> createStreamVolPair(int stream, int vol) { 215 return new Pair<>(stream, vol); 216 } 217 218 private void volumeVerificationPoll(Pair<Integer, Integer>... expectedStreamVolPairs) { 219 boolean isVolExpected = false; 220 int timeElapsedMs = 0; 221 try { 222 while (!isVolExpected && timeElapsedMs <= TIMEOUT_MS) { 223 Thread.sleep(POLL_INTERVAL_MS); 224 isVolExpected = true; 225 for (Pair<Integer, Integer> vol : expectedStreamVolPairs) { 226 if (mCarAudioManager.getStreamVolume(vol.first) != vol.second) { 227 isVolExpected = false; 228 break; 229 } 230 } 231 timeElapsedMs += POLL_INTERVAL_MS; 232 } 233 assertEquals(isVolExpected, true); 234 } catch (InterruptedException | CarNotConnectedException e) { 235 fail(e.toString()); 236 } 237 } 238 239 private void volumeChangeVerificationPoll(int stream, boolean showUI, 240 VolumeController controller) { 241 boolean isVolExpected = false; 242 int timeElapsedMs = 0; 243 try { 244 while (!isVolExpected && timeElapsedMs <= TIMEOUT_MS) { 245 Thread.sleep(POLL_INTERVAL_MS); 246 Pair<Integer, Integer> volChange = controller.getLastVolumeChanges(); 247 if (volChange.first == stream 248 && (((volChange.second.intValue() & AudioManager.FLAG_SHOW_UI) != 0) 249 == showUI)) { 250 isVolExpected = true; 251 break; 252 } 253 timeElapsedMs += POLL_INTERVAL_MS; 254 } 255 assertEquals(true, isVolExpected); 256 } catch (Exception e) { 257 fail(e.toString()); 258 } 259 } 260 261 private class SingleChannelVolumeHandler implements VehicleHalPropertyHandler { 262 private final List<Integer> mMaxs; 263 private final SparseIntArray mCurrent; 264 265 public SingleChannelVolumeHandler(List<Integer> maxs) { 266 mMaxs = maxs; 267 int size = maxs.size(); 268 mCurrent = new SparseIntArray(size); 269 // initialize the vol to be the min volume. 270 for (int i = 0; i < size; i++) { 271 mCurrent.put(i, mMaxs.get(i)); 272 } 273 } 274 275 @Override 276 public void onPropertySet(VehiclePropValue value) { 277 ArrayList<Integer> v = value.value.int32Values; 278 int stream = v.get(VehicleAudioVolumeIndex.INDEX_STREAM); 279 int volume = v.get(VehicleAudioVolumeIndex.INDEX_VOLUME); 280 int state = v.get(VehicleAudioVolumeIndex.INDEX_STATE); 281 282 mCurrent.put(stream, volume); 283 getMockedVehicleHal().injectEvent( 284 VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME) 285 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 286 .addIntValue(stream, volume, state) 287 .build()); 288 } 289 290 @Override 291 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 292 int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.INDEX_STREAM); 293 int volume = mCurrent.get(stream); 294 return VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME) 295 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 296 .addIntValue(stream, volume, 0) 297 .build(); 298 } 299 300 @Override 301 public void onPropertySubscribe(int property, int zones, float sampleRate) { 302 } 303 304 @Override 305 public void onPropertyUnsubscribe(int property) { 306 } 307 } 308 309 private final CarAudioFocusTest.FocusPropertyHandler mAudioFocusPropertyHandler = 310 new CarAudioFocusTest.FocusPropertyHandler(this); 311 312 private final VehicleHalPropertyHandler mAudioRoutingPolicyPropertyHandler = 313 new VehicleHalPropertyHandler() { 314 @Override 315 public void onPropertySet(VehiclePropValue value) { 316 //TODO 317 } 318 319 @Override 320 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 321 fail("cannot get"); 322 return null; 323 } 324 325 @Override 326 public void onPropertySubscribe(int property, int zones, float sampleRate) { 327 fail("cannot subscribe"); 328 } 329 330 @Override 331 public void onPropertyUnsubscribe(int property) { 332 fail("cannot unsubscribe"); 333 } 334 }; 335 336 private final VehicleHalPropertyHandler mHWKeyHandler = new VehicleHalPropertyHandler() { 337 @Override 338 public void onPropertySet(VehiclePropValue value) { 339 //TODO 340 } 341 342 @Override 343 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 344 return VehiclePropValueBuilder.newBuilder(VehicleProperty.HW_KEY_INPUT) 345 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 346 .addIntValue(0, 0, 0, 0) 347 .build(); 348 } 349 350 @Override 351 public void onPropertySubscribe(int property, int zones, float sampleRate) { 352 // 353 } 354 355 @Override 356 public void onPropertyUnsubscribe(int property) { 357 // 358 } 359 }; 360 361 private void startVolumeEmulation(int supportedAudioVolumeContext, List<Integer> maxs) { 362 SingleChannelVolumeHandler singleChannelVolumeHandler = 363 new SingleChannelVolumeHandler(maxs); 364 int zones = (1<<maxs.size()) - 1; 365 366 ArrayList<Integer> audioVolumeConfigArray = 367 Lists.newArrayList( 368 supportedAudioVolumeContext, 369 0 /* capability flag*/, 370 0, /* reserved */ 371 0 /* reserved */); 372 audioVolumeConfigArray.addAll(maxs); 373 374 addProperty(VehicleProperty.AUDIO_VOLUME, singleChannelVolumeHandler) 375 .setConfigArray(audioVolumeConfigArray) 376 .setSupportedAreas(zones); 377 378 addProperty(VehicleProperty.HW_KEY_INPUT, mHWKeyHandler) 379 .setAccess(VehiclePropertyAccess.READ); 380 381 addProperty(VehicleProperty.AUDIO_FOCUS, mAudioFocusPropertyHandler); 382 383 addProperty(VehicleProperty.AUDIO_ROUTING_POLICY, mAudioRoutingPolicyPropertyHandler) 384 .setAccess(VehiclePropertyAccess.WRITE); 385 386 addStaticProperty(VehicleProperty.AUDIO_HW_VARIANT, 387 VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_HW_VARIANT) 388 .addIntValue(-1) 389 .build()) 390 .setConfigArray(Lists.newArrayList(0)); 391 392 reinitializeMockedHal(); 393 } 394 395 private void sendVolumeKey(boolean volUp) { 396 int[] actionDown = { 397 VehicleHwKeyInputAction.ACTION_DOWN, 398 volUp ? KeyEvent.KEYCODE_VOLUME_UP : KeyEvent.KEYCODE_VOLUME_DOWN, 0, 0}; 399 400 VehiclePropValue injectValue = 401 VehiclePropValueBuilder.newBuilder(VehicleProperty.HW_KEY_INPUT) 402 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 403 .addIntValue(actionDown) 404 .build(); 405 406 getMockedVehicleHal().injectEvent(injectValue); 407 408 int[] actionUp = { 409 VehicleHwKeyInputAction.ACTION_UP, 410 volUp ? KeyEvent.KEYCODE_VOLUME_UP : KeyEvent.KEYCODE_VOLUME_DOWN, 0, 0 }; 411 412 injectValue = 413 VehiclePropValueBuilder.newBuilder(VehicleProperty.HW_KEY_INPUT) 414 .setTimestamp(SystemClock.elapsedRealtimeNanos()) 415 .addIntValue(actionUp) 416 .build(); 417 418 getMockedVehicleHal().injectEvent(injectValue); 419 } 420 421 private static class VolumeController extends IVolumeController.Stub { 422 @GuardedBy("this") 423 private int mLastStreamChanged = -1; 424 425 @GuardedBy("this") 426 private int mLastFlags = -1; 427 428 public synchronized Pair<Integer, Integer> getLastVolumeChanges() { 429 return new Pair<>(mLastStreamChanged, mLastFlags); 430 } 431 432 @Override 433 public void displaySafeVolumeWarning(int flags) throws RemoteException {} 434 435 @Override 436 public void volumeChanged(int streamType, int flags) throws RemoteException { 437 synchronized (this) { 438 mLastStreamChanged = streamType; 439 mLastFlags = flags; 440 } 441 } 442 443 @Override 444 public void masterMuteChanged(int flags) throws RemoteException {} 445 446 @Override 447 public void setLayoutDirection(int layoutDirection) throws RemoteException { 448 } 449 450 @Override 451 public void dismiss() throws RemoteException { 452 } 453 454 @Override 455 public void setA11yMode(int mode) throws RemoteException { 456 } 457 } 458} 459