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