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