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