1/*
2 * Copyright (C) 2015 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.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS;
19import static com.android.car.test.AudioTestUtils.doRequestFocus;
20
21import android.car.Car;
22import android.car.media.CarAudioManager;
23import android.content.Context;
24import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
25import android.hardware.automotive.vehicle.V2_0.VehicleAudioExtFocusFlag;
26import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusIndex;
27import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusRequest;
28import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusState;
29import android.hardware.automotive.vehicle.V2_0.VehicleAudioStream;
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.os.SystemClock;
36import android.test.suitebuilder.annotation.MediumTest;
37import android.util.Log;
38
39import com.google.android.collect.Lists;
40
41import com.android.car.vehiclehal.VehiclePropValueBuilder;
42import com.android.car.vehiclehal.test.MockedVehicleHal.FailingPropertyHandler;
43import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
44
45import java.util.ArrayList;
46import java.util.concurrent.Semaphore;
47import java.util.concurrent.TimeUnit;
48
49// TODO: refactor all CarAudio*Test classes, they have a lot of common logic.
50@MediumTest
51public class CarAudioFocusTest 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
74        addProperty(VehicleProperty.AUDIO_FOCUS, mAudioFocusPropertyHandler);
75
76        addStaticProperty(VehicleProperty.AUDIO_HW_VARIANT,
77                VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_HW_VARIANT)
78                        .addIntValue(-1)
79                        .build())
80                .setConfigArray(Lists.newArrayList(0));
81    }
82
83    @Override
84    protected void setUp() throws Exception {
85        super.setUp();
86        // AudioManager should be created in main thread to get focus event. :(
87        runOnMainSync(new Runnable() {
88            @Override
89            public void run() {
90                mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
91            }
92        });
93    }
94
95    public void testMediaGainFocus() throws Exception {
96        //TODO update this to check config
97        checkSingleRequestRelease(
98                AudioManager.STREAM_MUSIC,
99                AudioManager.AUDIOFOCUS_GAIN,
100                VehicleAudioStream.STREAM0,
101                VehicleAudioContextFlag.MUSIC_FLAG);
102    }
103
104    public void testMediaGainTransientFocus() throws Exception {
105        checkSingleRequestRelease(
106                AudioManager.STREAM_MUSIC,
107                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
108                VehicleAudioStream.STREAM0,
109                VehicleAudioContextFlag.MUSIC_FLAG);
110    }
111
112    public void testMediaGainTransientMayDuckFocus() throws Exception {
113        checkSingleRequestRelease(
114                AudioManager.STREAM_MUSIC,
115                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
116                VehicleAudioStream.STREAM0,
117                VehicleAudioContextFlag.MUSIC_FLAG);
118    }
119
120    public void testAlarmGainTransientFocus() throws Exception {
121        checkSingleRequestRelease(
122                AudioManager.STREAM_ALARM,
123                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
124                VehicleAudioStream.STREAM1,
125                VehicleAudioContextFlag.ALARM_FLAG);
126    }
127
128    public void testAlarmGainTransientMayDuckFocus() throws Exception {
129        checkSingleRequestRelease(
130                AudioManager.STREAM_ALARM,
131                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
132                VehicleAudioStream.STREAM1,
133                VehicleAudioContextFlag.ALARM_FLAG);
134    }
135
136    public void testMediaNavFocus() throws Exception {
137        //music start
138        AudioFocusListener listenerMusic = new AudioFocusListener();
139        int res = doRequestFocus(mAudioManager, listenerMusic,
140                AudioManager.STREAM_MUSIC,
141                AudioManager.AUDIOFOCUS_GAIN);
142        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
143        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
144        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
145        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
146        assertEquals(0, request[2]);
147        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
148        mAudioFocusPropertyHandler.sendAudioFocusState(
149                VehicleAudioFocusState.STATE_GAIN,
150                request[1],
151                VehicleAudioExtFocusFlag.NONE_FLAG);
152
153        // nav guidance start
154        AudioFocusListener listenerNav = new AudioFocusListener();
155        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
156                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
157                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
158                build();
159        doRequestFocus(mAudioManager, listenerNav, navAttrib,
160                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
161        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
162        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
163        assertEquals(0x3, request[1]);
164        assertEquals(0, request[2]);
165        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG |
166                VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]);
167        mAudioFocusPropertyHandler.sendAudioFocusState(
168                VehicleAudioFocusState.STATE_GAIN, request[1],
169                VehicleAudioExtFocusFlag.NONE_FLAG);
170
171        // nav guidance done
172        mAudioManager.abandonAudioFocus(listenerNav);
173        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
174        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
175        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
176        assertEquals(0, request[2]);
177        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
178        mAudioFocusPropertyHandler.sendAudioFocusState(
179                VehicleAudioFocusState.STATE_GAIN, request[1],
180                VehicleAudioExtFocusFlag.NONE_FLAG);
181
182        // music done
183        mAudioManager.abandonAudioFocus(listenerMusic);
184        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
185        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
186        assertEquals(0, request[1]);
187        assertEquals(0, request[2]);
188        assertEquals(0, request[3]);
189        mAudioFocusPropertyHandler.sendAudioFocusState(
190                VehicleAudioFocusState.STATE_LOSS, request[1],
191                VehicleAudioExtFocusFlag.NONE_FLAG);
192    }
193
194    public void testMediaExternalMediaNavFocus() throws Exception {
195        // android music
196        AudioFocusListener listenerMusic = new AudioFocusListener();
197        int res = doRequestFocus(mAudioManager, listenerMusic,
198                AudioManager.STREAM_MUSIC,
199                AudioManager.AUDIOFOCUS_GAIN);
200        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
201        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
202        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
203        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
204        assertEquals(0, request[2]);
205        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
206        mAudioFocusPropertyHandler.sendAudioFocusState(
207                VehicleAudioFocusState.STATE_GAIN,
208                request[1],
209                VehicleAudioExtFocusFlag.NONE_FLAG);
210
211        // car plays external media (=outside Android)
212        mAudioFocusPropertyHandler.sendAudioFocusState(
213                VehicleAudioFocusState.STATE_LOSS,
214                0,
215                VehicleAudioExtFocusFlag.PERMANENT_FLAG);
216        int focusChange = listenerMusic.waitAndGetFocusChange(TIMEOUT_MS);
217        assertEquals(AudioManager.AUDIOFOCUS_LOSS, focusChange);
218
219        // nav guidance start
220        AudioFocusListener listenerNav = new AudioFocusListener();
221        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
222                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
223                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
224                build();
225        doRequestFocus(mAudioManager, listenerNav, navAttrib,
226                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
227        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
228        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
229                request[0]);
230        assertEquals(0x1 << VehicleAudioStream.STREAM1, request[1]);
231        assertEquals(0, request[2]);
232        assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]);
233        mAudioFocusPropertyHandler.sendAudioFocusState(
234                VehicleAudioFocusState.STATE_GAIN_TRANSIENT,
235                0x1 << VehicleAudioStream.STREAM1,
236                VehicleAudioExtFocusFlag.PERMANENT_FLAG);
237
238        // nav guidance ends
239        mAudioManager.abandonAudioFocus(listenerNav);
240        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
241        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
242        assertEquals(0, request[1]);
243        assertEquals(0, request[2]);
244        assertEquals(0, request[3]);
245        mAudioFocusPropertyHandler.sendAudioFocusState(
246                VehicleAudioFocusState.STATE_LOSS,
247                0,
248                VehicleAudioExtFocusFlag.PERMANENT_FLAG);
249
250        // now ends external play
251        mAudioFocusPropertyHandler.sendAudioFocusState(
252                VehicleAudioFocusState.STATE_LOSS,
253                0,
254                0);
255        mAudioManager.abandonAudioFocus(listenerMusic);
256        //TODO how to check this?
257    }
258
259    public void testMediaExternalRadioNavMediaFocus() throws Exception {
260        // android music
261        AudioFocusListener listenerMusic = new AudioFocusListener();
262        int res = doRequestFocus(mAudioManager, listenerMusic,
263                AudioManager.STREAM_MUSIC,
264                AudioManager.AUDIOFOCUS_GAIN);
265        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
266        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
267        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
268        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
269        assertEquals(0, request[2]);
270        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
271        mAudioFocusPropertyHandler.sendAudioFocusState(
272                VehicleAudioFocusState.STATE_GAIN,
273                request[1],
274                VehicleAudioExtFocusFlag.NONE_FLAG);
275
276        // android radio
277        AudioFocusListener listenerRadio = new AudioFocusListener();
278        CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager(
279                Car.AUDIO_SERVICE);
280        assertNotNull(carAudioManager);
281        AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
282                CarAudioManager.CAR_AUDIO_USAGE_RADIO);
283        res = doRequestFocus(mAudioManager, listenerRadio,
284                radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
285        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
286        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
287        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
288        assertEquals(0, request[1]);
289        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
290                request[2]);
291        assertEquals(VehicleAudioContextFlag.RADIO_FLAG, request[3]);
292        mAudioFocusPropertyHandler.sendAudioFocusState(
293                VehicleAudioFocusState.STATE_GAIN,
294                0,
295                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
296
297        // nav guidance start
298        AudioFocusListener listenerNav = new AudioFocusListener();
299        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
300                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
301                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
302                build();
303        res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
304                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
305        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
306        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
307                request[0]);
308        assertEquals(0x1 << VehicleAudioStream.STREAM1, request[1]);
309        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
310                request[2]);
311        assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG |
312                VehicleAudioContextFlag.RADIO_FLAG, request[3]);
313        mAudioFocusPropertyHandler.sendAudioFocusState(
314                VehicleAudioFocusState.STATE_GAIN,
315                0x1 << VehicleAudioStream.STREAM1,
316                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
317
318        // nav guidance ends
319        mAudioManager.abandonAudioFocus(listenerNav);
320        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
321        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
322                request[0]);
323        assertEquals(0, request[1]);
324        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
325                request[2]);
326        assertEquals(VehicleAudioContextFlag.RADIO_FLAG, request[3]);
327        mAudioFocusPropertyHandler.sendAudioFocusState(
328                VehicleAudioFocusState.STATE_GAIN,
329                0,
330                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
331
332        // ends radio. music will get the focus GAIN.
333        // Music app is supposed to stop and release focus when it has lost focus, but here just
334        // check if focus is working.
335        mAudioManager.abandonAudioFocus(listenerRadio);
336        listenerMusic.waitForFocus(TIMEOUT_MS, AudioManager.AUDIOFOCUS_GAIN);
337        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
338        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
339        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
340        assertEquals(0, request[2]);
341        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
342        mAudioFocusPropertyHandler.sendAudioFocusState(
343                VehicleAudioFocusState.STATE_GAIN,
344                0x1 << VehicleAudioStream.STREAM0,
345                0);
346
347        // now music release focus.
348        mAudioManager.abandonAudioFocus(listenerMusic);
349        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
350        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
351        assertEquals(0, request[1]);
352        assertEquals(0, request[2]);
353        assertEquals(0, request[3]);
354        mAudioFocusPropertyHandler.sendAudioFocusState(
355                VehicleAudioFocusState.STATE_LOSS,
356                0,
357                VehicleAudioExtFocusFlag.NONE_FLAG);
358    }
359
360    private void checkSingleRequestRelease(int streamType, int androidFocus, int streamNumber,
361            int context)
362            throws Exception {
363        AudioFocusListener lister = new AudioFocusListener();
364        int res = doRequestFocus(mAudioManager, lister,
365                streamType,
366                androidFocus);
367        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
368        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
369        int expectedRequest = VehicleAudioFocusRequest.REQUEST_RELEASE;
370        int response = VehicleAudioFocusState.STATE_LOSS;
371        switch (androidFocus) {
372            case AudioManager.AUDIOFOCUS_GAIN:
373                expectedRequest = VehicleAudioFocusRequest.REQUEST_GAIN;
374                response = VehicleAudioFocusState.STATE_GAIN;
375                break;
376            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
377                expectedRequest =
378                    VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT;
379                response = VehicleAudioFocusState.STATE_GAIN_TRANSIENT;
380                break;
381            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
382                expectedRequest =
383                    VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK;
384                response = VehicleAudioFocusState.STATE_GAIN_TRANSIENT;
385                break;
386        }
387        assertEquals(expectedRequest, request[0]);
388        assertEquals(0x1 << streamNumber, request[1]);
389        assertEquals(0, request[2]);
390        assertEquals(context, request[3]);
391        mAudioFocusPropertyHandler.sendAudioFocusState(
392                response,
393                request[1],
394                VehicleAudioExtFocusFlag.NONE_FLAG);
395        mAudioManager.abandonAudioFocus(lister);
396        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
397        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
398        assertEquals(0, request[1]);
399        assertEquals(0, request[2]);
400        assertEquals(0, request[3]);
401        mAudioFocusPropertyHandler.sendAudioFocusState(
402                VehicleAudioFocusState.STATE_LOSS,
403                request[1],
404                VehicleAudioExtFocusFlag.NONE_FLAG);
405    }
406
407    public void testRadioMute() throws Exception {
408        doTestMediaMute(CarAudioManager.CAR_AUDIO_USAGE_RADIO,
409                0,
410                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
411                VehicleAudioContextFlag.RADIO_FLAG);
412    }
413
414    public void testMusicMute() throws Exception {
415        doTestMediaMute(CarAudioManager.CAR_AUDIO_USAGE_MUSIC,
416                0x1,
417                0,
418                VehicleAudioContextFlag.MUSIC_FLAG);
419    }
420
421    private void doTestMediaMute(int mediaUsage, int primaryStream, int extFocusFlag,
422            int mediaContext) throws Exception {
423        // android radio
424        AudioFocusListener listenerMedia = new AudioFocusListener();
425        CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager(
426                Car.AUDIO_SERVICE);
427        assertNotNull(carAudioManager);
428        AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(mediaUsage);
429        Log.i(TAG, "request media Focus");
430        int res = doRequestFocus(mAudioManager, listenerMedia,
431                radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
432        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
433        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
434        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
435        assertEquals(primaryStream, request[1]);
436        assertEquals(extFocusFlag, request[2]);
437        assertEquals(mediaContext, request[3]);
438        mAudioFocusPropertyHandler.sendAudioFocusState(
439                VehicleAudioFocusState.STATE_GAIN,
440                primaryStream,
441                extFocusFlag);
442        // now mute it.
443        assertFalse(carAudioManager.isMediaMuted());
444        Log.i(TAG, "mute media");
445        assertTrue(carAudioManager.setMediaMute(true));
446        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
447        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT,
448                request[0]);
449        assertEquals(0, request[1]);
450        assertEquals(VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG,
451                request[2]);
452        assertEquals(0, request[3]);
453        mAudioFocusPropertyHandler.sendAudioFocusState(
454                VehicleAudioFocusState.STATE_GAIN,
455                0,
456                VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG);
457        assertTrue(carAudioManager.isMediaMuted());
458        // nav guidance on top of it
459        AudioFocusListener listenerNav = new AudioFocusListener();
460        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
461                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
462                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
463                build();
464        Log.i(TAG, "request nav Focus");
465        res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
466                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
467        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
468        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
469                request[0]);
470        assertEquals(0x1 << VehicleAudioStream.STREAM1, request[1]);
471        assertEquals(VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG,
472                request[2]);
473        assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]);
474        assertTrue(carAudioManager.isMediaMuted());
475        mAudioFocusPropertyHandler.sendAudioFocusState(
476                VehicleAudioFocusState.STATE_GAIN,
477                0x1 << VehicleAudioStream.STREAM1,
478                VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG);
479        assertTrue(carAudioManager.isMediaMuted());
480        // nav guidance ends
481        mAudioManager.abandonAudioFocus(listenerNav);
482        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
483        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT,
484                request[0]);
485        assertEquals(0, request[1]);
486        assertEquals(VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG,
487                request[2]);
488        assertEquals(0, request[3]);
489        assertTrue(carAudioManager.isMediaMuted());
490        mAudioFocusPropertyHandler.sendAudioFocusState(
491                VehicleAudioFocusState.STATE_GAIN,
492                0,
493                VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG);
494        // now unmute it. radio should resume.
495        assertTrue(carAudioManager.isMediaMuted());
496        assertFalse(carAudioManager.setMediaMute(false));
497        assertFalse(carAudioManager.isMediaMuted());
498        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
499        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
500        assertEquals(primaryStream, request[1]);
501        assertEquals(extFocusFlag,
502                request[2]);
503        assertEquals(mediaContext, request[3]);
504        assertFalse(carAudioManager.isMediaMuted());
505        mAudioFocusPropertyHandler.sendAudioFocusState(
506                VehicleAudioFocusState.STATE_GAIN,
507                primaryStream,
508                extFocusFlag);
509        assertFalse(carAudioManager.isMediaMuted());
510        // release focus
511        mAudioManager.abandonAudioFocus(listenerMedia);
512    }
513
514    static class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
515        private final Semaphore mFocusChangeWait = new Semaphore(0);
516        private int mLastFocusChange;
517
518        int waitAndGetFocusChange(long timeoutMs) throws Exception {
519            if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
520                fail("timeout waiting for focus change");
521            }
522            return mLastFocusChange;
523        }
524
525        void waitForFocus(long timeoutMs, int expectedFocus) throws Exception {
526            while (mLastFocusChange != expectedFocus) {
527                if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
528                    fail("timeout waiting for focus change");
529                }
530            }
531        }
532
533        @Override
534        public void onAudioFocusChange(int focusChange) {
535            mLastFocusChange = focusChange;
536            mFocusChangeWait.release();
537        }
538    }
539
540    static class FocusPropertyHandler implements VehicleHalPropertyHandler {
541
542        private int mState = VehicleAudioFocusState.STATE_LOSS;
543        private int mStreams = 0;
544        private int mExtFocus = 0;
545        private int mRequest;
546        private int mRequestedStreams;
547        private int mRequestedExtFocus;
548        private int mRequestedAudioContexts;
549        private final MockedCarTestBase mCarTest;
550
551        private final Semaphore mSetWaitSemaphore = new Semaphore(0);
552
553        FocusPropertyHandler(MockedCarTestBase carTest) {
554            mCarTest = carTest;
555        }
556
557        void sendAudioFocusState(int state, int streams, int extFocus) {
558            synchronized (this) {
559                mState = state;
560                mStreams = streams;
561                mExtFocus = extFocus;
562            }
563            mCarTest.getMockedVehicleHal().injectEvent(
564                    VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_FOCUS)
565                            .setTimestamp(SystemClock.elapsedRealtimeNanos())
566                            .addIntValue(state, streams, extFocus, 0)
567                            .build());
568        }
569
570        int[] waitForAudioFocusRequest(long timeoutMs) throws Exception {
571            if (!mSetWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
572                fail("timeout");
573            }
574            synchronized (this) {
575                return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus,
576                        mRequestedAudioContexts };
577            }
578        }
579
580        @Override
581        public void onPropertySet(VehiclePropValue value) {
582            assertEquals(VehicleProperty.AUDIO_FOCUS, value.prop);
583            ArrayList<Integer> v = value.value.int32Values;
584            synchronized (this) {
585                mRequest = v.get(VehicleAudioFocusIndex.FOCUS);
586                mRequestedStreams = v.get(VehicleAudioFocusIndex.STREAMS);
587                mRequestedExtFocus = v.get(VehicleAudioFocusIndex.EXTERNAL_FOCUS_STATE);
588                mRequestedAudioContexts = v.get(VehicleAudioFocusIndex.AUDIO_CONTEXTS);
589            }
590            mSetWaitSemaphore.release();
591        }
592
593        @Override
594        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
595            assertEquals(VehicleProperty.AUDIO_FOCUS, value.prop);
596            int state, streams, extFocus;
597            synchronized (this) {
598                state = mState;
599                streams = mStreams;
600                extFocus = mExtFocus;
601            }
602            return VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_FOCUS)
603                    .setTimestamp(SystemClock.elapsedRealtimeNanos())
604                    .addIntValue(state, streams, extFocus, 0)
605                    .build();
606        }
607
608        @Override
609        public void onPropertySubscribe(int property, int zones, float sampleRate) {
610            assertEquals(AUDIO_FOCUS, property);
611        }
612
613        @Override
614        public void onPropertyUnsubscribe(int property) {
615            assertEquals(AUDIO_FOCUS, property);
616        }
617    }
618}
619