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