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.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS;
19import static com.android.car.test.AudioTestUtils.doRequestFocus;
20import static java.lang.Integer.toHexString;
21
22import android.car.Car;
23import android.car.media.CarAudioManager;
24import android.content.Context;
25import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
26import android.hardware.automotive.vehicle.V2_0.VehicleAudioExtFocusFlag;
27import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusIndex;
28import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusRequest;
29import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusState;
30import android.hardware.automotive.vehicle.V2_0.VehicleAudioStream;
31import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
32import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
33import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
34import android.media.AudioAttributes;
35import android.media.AudioManager;
36import android.os.SystemClock;
37import android.test.suitebuilder.annotation.MediumTest;
38import android.util.Log;
39
40import com.google.android.collect.Lists;
41
42import com.android.car.vehiclehal.VehiclePropValueBuilder;
43import com.android.car.vehiclehal.test.MockedVehicleHal.FailingPropertyHandler;
44import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
45
46import java.util.ArrayList;
47import java.util.Arrays;
48import java.util.LinkedList;
49import java.util.concurrent.Semaphore;
50import java.util.concurrent.TimeUnit;
51
52@MediumTest
53public class CarAudioExtFocusTest extends MockedCarTestBase {
54    private static final String TAG = CarAudioExtFocusTest.class.getSimpleName();
55
56    private static final long TIMEOUT_MS = 3000;
57
58    private final VehicleHalPropertyHandler mAudioRoutingPolicyPropertyHandler =
59            new FailingPropertyHandler() {
60        @Override
61        public void onPropertySet(VehiclePropValue value) {
62            //TODO
63        }
64    };
65
66    private final FocusPropertyHandler mAudioFocusPropertyHandler =
67            new FocusPropertyHandler(this);
68
69    private final ExtRoutingHintPropertyHandler mExtRoutingHintPropertyHandler =
70            new ExtRoutingHintPropertyHandler();
71
72    private static final String EXT_ROUTING_CONFIG =
73            "0:RADIO_AM_FM:0,1:RADIO_SATELLITE:0,33:CD_DVD:0," +
74            "64:com.google.test.SOMETHING_SPECIAL," +
75            "4:EXT_NAV_GUIDANCE:1," +
76            "5:AUX_IN0:0";
77
78    private final Semaphore mWaitSemaphore = new Semaphore(0);
79    private final LinkedList<VehiclePropValue> mEvents = new LinkedList<VehiclePropValue>();
80    private AudioManager mAudioManager;
81    private CarAudioManager mCarAudioManager;
82
83    @Override
84    protected synchronized void configureMockedHal() {
85        addProperty(VehicleProperty.AUDIO_ROUTING_POLICY, mAudioRoutingPolicyPropertyHandler);
86        addProperty(VehicleProperty.AUDIO_FOCUS, mAudioFocusPropertyHandler);
87
88        addStaticProperty(VehicleProperty.AUDIO_HW_VARIANT,
89                VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_HW_VARIANT)
90                        .addIntValue(-1)
91                        .build())
92                .setConfigArray(Lists.newArrayList(0));
93
94        addProperty(VehicleProperty.AUDIO_EXT_ROUTING_HINT, mExtRoutingHintPropertyHandler)
95                .setAccess(VehiclePropertyAccess.WRITE)
96                .setConfigString(EXT_ROUTING_CONFIG);
97    }
98
99    @Override
100    protected void setUp() throws Exception {
101        super.setUp();
102        // AudioManager should be created in main thread to get focus event. :(
103        runOnMainSync(new Runnable() {
104            @Override
105            public void run() {
106                mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
107            }
108        });
109
110        mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
111        assertNotNull(mCarAudioManager);
112    }
113
114    public void testExtRoutings() throws Exception {
115        String[] radioTypes = mCarAudioManager.getSupportedRadioTypes();
116        assertNotNull(radioTypes);
117        checkStringArrayContents(new String[] {"RADIO_AM_FM", "RADIO_SATELLITE"}, radioTypes);
118
119        String[] nonRadioTypes = mCarAudioManager.getSupportedExternalSourceTypes();
120        assertNotNull(nonRadioTypes);
121        checkStringArrayContents(new String[] {"CD_DVD", "com.google.test.SOMETHING_SPECIAL",
122                "EXT_NAV_GUIDANCE", "AUX_IN0"}, nonRadioTypes);
123    }
124
125    private void checkStringArrayContents(String[] expected, String[] actual) throws Exception {
126        Arrays.sort(expected);
127        Arrays.sort(actual);
128        assertEquals(expected.length, actual.length);
129        for (int i = 0; i < expected.length; i++) {
130            assertEquals(expected[i], actual[i]);
131        }
132    }
133
134    public void testRadioAttributeCreation() throws Exception {
135        AudioAttributes attrb = mCarAudioManager.getAudioAttributesForRadio(
136                CarAudioManager.CAR_RADIO_TYPE_AM_FM);
137        assertNotNull(attrb);
138
139        attrb = mCarAudioManager.getAudioAttributesForRadio(
140                CarAudioManager.CAR_RADIO_TYPE_SATELLITE);
141        assertNotNull(attrb);
142
143        try {
144            mCarAudioManager.getAudioAttributesForRadio(CarAudioManager.CAR_RADIO_TYPE_AM_FM_HD);
145            fail();
146        } catch (IllegalArgumentException e) {
147            // expected
148        }
149    }
150
151    public void testExtSourceAttributeCreation() throws Exception {
152        AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
153                CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_CD_DVD);
154        assertNotNull(attrb);
155
156        attrb = mCarAudioManager.getAudioAttributesForExternalSource(
157                CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
158        assertNotNull(attrb);
159
160        attrb = mCarAudioManager.getAudioAttributesForExternalSource(
161                "com.google.test.SOMETHING_SPECIAL");
162        assertNotNull(attrb);
163
164        try {
165            mCarAudioManager.getAudioAttributesForExternalSource(
166                    CarAudioManager.CAR_RADIO_TYPE_AM_FM_HD);
167            fail();
168        } catch (IllegalArgumentException e) {
169            // expected
170        }
171    }
172
173    public void testRadioAmFmGainFocus() throws Exception {
174        AudioAttributes attrb = mCarAudioManager.getAudioAttributesForRadio(
175                CarAudioManager.CAR_RADIO_TYPE_AM_FM);
176        assertNotNull(attrb);
177        checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {1, 0, 0, 0},
178                0, VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
179                VehicleAudioContextFlag.RADIO_FLAG);
180    }
181
182    public void testRadioSatelliteGainFocus() throws Exception {
183        AudioAttributes attrb = mCarAudioManager.getAudioAttributesForRadio(
184                CarAudioManager.CAR_RADIO_TYPE_SATELLITE);
185        assertNotNull(attrb);
186        checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {2, 0, 0, 0},
187                0, VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
188                VehicleAudioContextFlag.RADIO_FLAG);
189    }
190
191    public void testCdGainFocus() throws Exception {
192        AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
193                CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_CD_DVD);
194        assertNotNull(attrb);
195        checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {0, 2, 0, 0},
196                0, VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
197                VehicleAudioContextFlag.CD_ROM_FLAG);
198    }
199
200    public void testAuxInFocus() throws Exception {
201        AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
202                CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_AUX_IN0);
203        assertNotNull(attrb);
204        checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {0x1<<5, 0, 0, 0},
205                0, VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
206                VehicleAudioContextFlag.AUX_AUDIO_FLAG);
207    }
208
209    public void testExtNavInFocus() throws Exception {
210        AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
211                CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
212        assertNotNull(attrb);
213        checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {0x1<<4, 0, 0, 0},
214                0, VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
215                VehicleAudioContextFlag.EXT_SOURCE_FLAG);
216    }
217
218    public void testCustomInFocus() throws Exception {
219        AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
220                "com.google.test.SOMETHING_SPECIAL");
221        assertNotNull(attrb);
222        checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {0, 0, 1, 0},
223                0, VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
224                VehicleAudioContextFlag.EXT_SOURCE_FLAG);
225    }
226
227    public void testMediaNavFocus() throws Exception {
228        //music start
229        AudioFocusListener listenerMusic = new AudioFocusListener();
230        int res = doRequestFocus(mAudioManager, listenerMusic,
231                AudioManager.STREAM_MUSIC,
232                AudioManager.AUDIOFOCUS_GAIN);
233        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
234        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
235        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
236        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
237        assertEquals(0, request[2]);
238        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
239        assertArrayEquals(new int[] {0, 0, 0, 0},
240                mExtRoutingHintPropertyHandler.getLastHint());
241        mAudioFocusPropertyHandler.sendAudioFocusState(
242                VehicleAudioFocusState.STATE_GAIN,
243                request[1],
244                VehicleAudioExtFocusFlag.NONE_FLAG);
245
246        // nav guidance start
247        AudioFocusListener listenerNav = new AudioFocusListener();
248        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
249                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
250                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
251                build();
252        doRequestFocus(mAudioManager, listenerNav, navAttrib,
253                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
254        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
255        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
256        assertEquals(0x3, request[1]);
257        assertEquals(0, request[2]);
258        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG |
259                VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]);
260        assertArrayEquals(new int[] {0, 0, 0, 0},
261                mExtRoutingHintPropertyHandler.getLastHint());
262        mAudioFocusPropertyHandler.sendAudioFocusState(
263                VehicleAudioFocusState.STATE_GAIN, request[1],
264                VehicleAudioExtFocusFlag.NONE_FLAG);
265
266        // nav guidance done
267        mAudioManager.abandonAudioFocus(listenerNav);
268        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
269        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
270        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
271        assertEquals(0, request[2]);
272        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
273        assertArrayEquals(new int[] {0, 0, 0, 0},
274                mExtRoutingHintPropertyHandler.getLastHint());
275        mAudioFocusPropertyHandler.sendAudioFocusState(
276                VehicleAudioFocusState.STATE_GAIN, request[1],
277                VehicleAudioExtFocusFlag.NONE_FLAG);
278
279        // music done
280        mAudioManager.abandonAudioFocus(listenerMusic);
281        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
282        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
283        assertEquals(0, request[1]);
284        assertEquals(0, request[2]);
285        assertEquals(0, request[3]);
286        assertArrayEquals(new int[] {0, 0, 0, 0},
287                mExtRoutingHintPropertyHandler.getLastHint());
288        mAudioFocusPropertyHandler.sendAudioFocusState(
289                VehicleAudioFocusState.STATE_LOSS, request[1],
290                VehicleAudioExtFocusFlag.NONE_FLAG);
291    }
292
293    public void testMediaExternalMediaNavFocus() throws Exception {
294        // android music
295        AudioFocusListener listenerMusic = new AudioFocusListener();
296        int res = doRequestFocus(mAudioManager, listenerMusic,
297                AudioManager.STREAM_MUSIC,
298                AudioManager.AUDIOFOCUS_GAIN);
299        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
300        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
301        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
302        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
303        assertEquals(0, request[2]);
304        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
305        assertArrayEquals(new int[] {0, 0, 0, 0},
306                mExtRoutingHintPropertyHandler.getLastHint());
307        mAudioFocusPropertyHandler.sendAudioFocusState(
308                VehicleAudioFocusState.STATE_GAIN,
309                request[1],
310                VehicleAudioExtFocusFlag.NONE_FLAG);
311
312        // car plays external media (=outside Android)
313        mAudioFocusPropertyHandler.sendAudioFocusState(
314                VehicleAudioFocusState.STATE_LOSS,
315                0,
316                VehicleAudioExtFocusFlag.PERMANENT_FLAG);
317        int focusChange = listenerMusic.waitAndGetFocusChange(TIMEOUT_MS);
318        assertEquals(AudioManager.AUDIOFOCUS_LOSS, focusChange);
319
320        // nav guidance start
321        AudioFocusListener listenerNav = new AudioFocusListener();
322        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
323                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
324                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
325                build();
326        doRequestFocus(mAudioManager, listenerNav, navAttrib,
327                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
328        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
329        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
330                request[0]);
331        assertEquals(0x1 << VehicleAudioStream.STREAM1, request[1]);
332        assertEquals(0, request[2]);
333        assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]);
334        assertArrayEquals(new int[] {0, 0, 0, 0},
335                mExtRoutingHintPropertyHandler.getLastHint());
336        mAudioFocusPropertyHandler.sendAudioFocusState(
337                VehicleAudioFocusState.STATE_GAIN_TRANSIENT,
338                0x1 << VehicleAudioStream.STREAM1,
339                VehicleAudioExtFocusFlag.PERMANENT_FLAG);
340
341        // nav guidance ends
342        mAudioManager.abandonAudioFocus(listenerNav);
343        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
344        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
345        assertEquals(0, request[1]);
346        assertEquals(0, request[2]);
347        assertEquals(0, request[3]);
348        assertArrayEquals(new int[] {0, 0, 0, 0},
349                mExtRoutingHintPropertyHandler.getLastHint());
350        mAudioFocusPropertyHandler.sendAudioFocusState(
351                VehicleAudioFocusState.STATE_LOSS,
352                0,
353                VehicleAudioExtFocusFlag.PERMANENT_FLAG);
354
355        // now ends external play
356        mAudioFocusPropertyHandler.sendAudioFocusState(
357                VehicleAudioFocusState.STATE_LOSS,
358                0,
359                0);
360        mAudioManager.abandonAudioFocus(listenerMusic);
361        //TODO how to check this?
362    }
363
364    public void testExternalRadioExternalNav() throws Exception {
365        // android radio
366        AudioFocusListener listenerRadio = new AudioFocusListener();
367        CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager(
368                Car.AUDIO_SERVICE);
369        assertNotNull(carAudioManager);
370        AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
371                CarAudioManager.CAR_AUDIO_USAGE_RADIO);
372        int res = doRequestFocus(mAudioManager, listenerRadio,
373                radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
374        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
375        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
376        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
377        assertEquals(0, request[1]);
378        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
379                request[2]);
380        assertEquals(VehicleAudioContextFlag.RADIO_FLAG, request[3]);
381        assertArrayEquals(new int[] {1, 0, 0, 0},
382                mExtRoutingHintPropertyHandler.getLastHint());
383        mAudioFocusPropertyHandler.sendAudioFocusState(
384                VehicleAudioFocusState.STATE_GAIN,
385                0,
386                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
387
388        //external nav
389        AudioFocusListener listenerNav = new AudioFocusListener();
390        AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
391                CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
392        res = doRequestFocus(mAudioManager, listenerNav,
393                extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
394        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
395        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
396        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
397                request[0]);
398        assertEquals(0, request[1]);
399        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
400                request[2]);
401        assertEquals(VehicleAudioContextFlag.RADIO_FLAG |
402                VehicleAudioContextFlag.EXT_SOURCE_FLAG, request[3]);
403        assertArrayEquals(new int[] {1 | 1<<4, 0, 0, 0},
404                mExtRoutingHintPropertyHandler.getLastHint());
405        mAudioFocusPropertyHandler.sendAudioFocusState(
406                VehicleAudioFocusState.STATE_GAIN,
407                0,
408                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
409
410        mAudioManager.abandonAudioFocus(listenerNav);
411        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
412        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
413        assertEquals(0, request[1]);
414        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
415                request[2]);
416        assertEquals(VehicleAudioContextFlag.RADIO_FLAG, request[3]);
417        assertArrayEquals(new int[] {1, 0, 0, 0},
418                mExtRoutingHintPropertyHandler.getLastHint());
419        mAudioFocusPropertyHandler.sendAudioFocusState(
420                VehicleAudioFocusState.STATE_GAIN,
421                0,
422                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
423
424        mAudioManager.abandonAudioFocus(listenerRadio);
425        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
426        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
427        assertEquals(0, request[1]);
428        assertEquals(0, request[2]);
429        assertEquals(0, request[3]);
430        assertArrayEquals(new int[] {0, 0, 0, 0},
431                mExtRoutingHintPropertyHandler.getLastHint());
432        mAudioFocusPropertyHandler.sendAudioFocusState(
433                VehicleAudioFocusState.STATE_LOSS,
434                0,
435                0);
436    }
437
438    public void testMediaExternalNav() throws Exception {
439        // android music
440        AudioFocusListener listenerMusic = new AudioFocusListener();
441        int res = doRequestFocus(mAudioManager, listenerMusic,
442                AudioManager.STREAM_MUSIC,
443                AudioManager.AUDIOFOCUS_GAIN);
444        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
445        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
446        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
447        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
448        assertEquals(0, request[2]);
449        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
450        assertArrayEquals(new int[] {0, 0, 0, 0},
451                mExtRoutingHintPropertyHandler.getLastHint());
452        mAudioFocusPropertyHandler.sendAudioFocusState(
453                VehicleAudioFocusState.STATE_GAIN,
454                request[1],
455                VehicleAudioExtFocusFlag.NONE_FLAG);
456
457        //external nav
458        AudioFocusListener listenerNav = new AudioFocusListener();
459        AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
460                CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
461        res = doRequestFocus(mAudioManager, listenerNav,
462                extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
463        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
464        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
465        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
466                request[0]);
467        assertEquals(0x1, request[1]);
468        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
469                request[2]);
470        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG |
471                VehicleAudioContextFlag.EXT_SOURCE_FLAG, request[3]);
472        assertArrayEquals(new int[] {1<<4, 0, 0, 0},
473                mExtRoutingHintPropertyHandler.getLastHint());
474        mAudioFocusPropertyHandler.sendAudioFocusState(
475                VehicleAudioFocusState.STATE_GAIN,
476                0x1,
477                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
478
479        mAudioManager.abandonAudioFocus(listenerNav);
480        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
481        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
482        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
483        assertEquals(0, request[2]);
484        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
485        assertArrayEquals(new int[] {0, 0, 0, 0},
486                mExtRoutingHintPropertyHandler.getLastHint());
487        mAudioFocusPropertyHandler.sendAudioFocusState(
488                VehicleAudioFocusState.STATE_GAIN,
489                request[1],
490                VehicleAudioExtFocusFlag.NONE_FLAG);
491
492        mAudioManager.abandonAudioFocus(listenerMusic);
493        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
494        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
495        assertEquals(0, request[1]);
496        assertEquals(0, request[2]);
497        assertEquals(0, request[3]);
498        assertArrayEquals(new int[] {0, 0, 0, 0},
499                mExtRoutingHintPropertyHandler.getLastHint());
500        mAudioFocusPropertyHandler.sendAudioFocusState(
501                VehicleAudioFocusState.STATE_LOSS,
502                0,
503                0);
504    }
505
506    /**
507     * Test internal nav - external nav case.
508     * External nav takes the same physical stream as internal nav. So internal nav
509     * will be lost while external nav is played. This should not happen in real case when
510     * AppFocus is used, but this test is to make sure that audio focus works as expected.
511     */
512    public void testNavExternalNav() throws Exception {
513        // android nav
514        AudioFocusListener listenerIntNav = new AudioFocusListener();
515        AudioAttributes intNavAttributes = mCarAudioManager.getAudioAttributesForCarUsage(
516                CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE);
517        int res = doRequestFocus(mAudioManager, listenerIntNav, intNavAttributes,
518                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
519        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
520        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
521        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
522                request[0]);
523        assertEquals(0x2, request[1]);
524        assertEquals(0, request[2]);
525        assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]);
526        assertArrayEquals(new int[] {0, 0, 0, 0},
527                mExtRoutingHintPropertyHandler.getLastHint());
528        mAudioFocusPropertyHandler.sendAudioFocusState(
529                VehicleAudioFocusState.STATE_GAIN,
530                request[1],
531                VehicleAudioExtFocusFlag.NONE_FLAG);
532
533        //external nav
534        AudioFocusListener listenerExtNav = new AudioFocusListener();
535        AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
536                CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
537        res = doRequestFocus(mAudioManager, listenerExtNav,
538                extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
539        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
540        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
541        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
542                request[0]);
543        assertEquals(0, request[1]);
544        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
545                request[2]);
546        assertEquals(VehicleAudioContextFlag.EXT_SOURCE_FLAG, request[3]);
547        assertArrayEquals(new int[] {1<<4, 0, 0, 0},
548                mExtRoutingHintPropertyHandler.getLastHint());
549        mAudioFocusPropertyHandler.sendAudioFocusState(
550                VehicleAudioFocusState.STATE_GAIN,
551                0x1,
552                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
553
554        mAudioManager.abandonAudioFocus(listenerExtNav);
555        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
556        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
557                request[0]);
558        assertEquals(0x2, request[1]);
559        assertEquals(0, request[2]);
560        assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]);
561        assertArrayEquals(new int[] {0, 0, 0, 0},
562                mExtRoutingHintPropertyHandler.getLastHint());
563        mAudioFocusPropertyHandler.sendAudioFocusState(
564                VehicleAudioFocusState.STATE_GAIN,
565                request[1],
566                VehicleAudioExtFocusFlag.NONE_FLAG);
567
568        mAudioManager.abandonAudioFocus(listenerIntNav);
569        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
570        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
571        assertEquals(0, request[1]);
572        assertEquals(0, request[2]);
573        assertEquals(0, request[3]);
574        assertArrayEquals(new int[] {0, 0, 0, 0},
575                mExtRoutingHintPropertyHandler.getLastHint());
576        mAudioFocusPropertyHandler.sendAudioFocusState(
577                VehicleAudioFocusState.STATE_LOSS,
578                0,
579                0);
580    }
581
582    public void testMediaExternalRadioNavMediaFocus() throws Exception {
583        // android music
584        AudioFocusListener listenerMusic = new AudioFocusListener();
585        int res = doRequestFocus(mAudioManager, listenerMusic,
586                AudioManager.STREAM_MUSIC,
587                AudioManager.AUDIOFOCUS_GAIN);
588        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
589        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
590        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
591        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
592        assertEquals(0, request[2]);
593        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
594        assertArrayEquals(new int[] {0, 0, 0, 0},
595                mExtRoutingHintPropertyHandler.getLastHint());
596        mAudioFocusPropertyHandler.sendAudioFocusState(
597                VehicleAudioFocusState.STATE_GAIN,
598                request[1],
599                VehicleAudioExtFocusFlag.NONE_FLAG);
600
601        // android radio
602        AudioFocusListener listenerRadio = new AudioFocusListener();
603        CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager(
604                Car.AUDIO_SERVICE);
605        assertNotNull(carAudioManager);
606        AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
607                CarAudioManager.CAR_AUDIO_USAGE_RADIO);
608        res = doRequestFocus(mAudioManager, listenerRadio,
609                radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
610        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
611        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
612        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
613        assertEquals(0, request[1]);
614        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
615                request[2]);
616        assertEquals(VehicleAudioContextFlag.RADIO_FLAG, request[3]);
617        assertArrayEquals(new int[] {1, 0, 0, 0},
618                mExtRoutingHintPropertyHandler.getLastHint());
619        mAudioFocusPropertyHandler.sendAudioFocusState(
620                VehicleAudioFocusState.STATE_GAIN,
621                0,
622                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
623
624        // nav guidance start
625        AudioFocusListener listenerNav = new AudioFocusListener();
626        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
627                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
628                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
629                build();
630        res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
631                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
632        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
633        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
634                request[0]);
635        assertEquals(0x1 << VehicleAudioStream.STREAM1, request[1]);
636        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
637                request[2]);
638        assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG |
639                VehicleAudioContextFlag.RADIO_FLAG, request[3]);
640        assertArrayEquals(new int[] {1, 0, 0, 0},
641                mExtRoutingHintPropertyHandler.getLastHint());
642        mAudioFocusPropertyHandler.sendAudioFocusState(
643                VehicleAudioFocusState.STATE_GAIN,
644                0x1 << VehicleAudioStream.STREAM1,
645                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
646
647        // nav guidance ends
648        mAudioManager.abandonAudioFocus(listenerNav);
649        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
650        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
651                request[0]);
652        assertEquals(0, request[1]);
653        assertEquals(VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
654                request[2]);
655        assertEquals(VehicleAudioContextFlag.RADIO_FLAG, request[3]);
656        assertArrayEquals(new int[] {1, 0, 0, 0},
657                mExtRoutingHintPropertyHandler.getLastHint());
658        mAudioFocusPropertyHandler.sendAudioFocusState(
659                VehicleAudioFocusState.STATE_GAIN,
660                0,
661                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG);
662
663        // ends radio. music will get the focus GAIN.
664        // Music app is supposed to stop and release focus when it has lost focus, but here just
665        // check if focus is working.
666        mAudioManager.abandonAudioFocus(listenerRadio);
667        listenerMusic.waitForFocus(TIMEOUT_MS, AudioManager.AUDIOFOCUS_GAIN);
668        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
669        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
670        assertEquals(0x1 << VehicleAudioStream.STREAM0, request[1]);
671        assertEquals(0, request[2]);
672        assertEquals(VehicleAudioContextFlag.MUSIC_FLAG, request[3]);
673        assertArrayEquals(new int[] {0, 0, 0, 0},
674                mExtRoutingHintPropertyHandler.getLastHint());
675        mAudioFocusPropertyHandler.sendAudioFocusState(
676                VehicleAudioFocusState.STATE_GAIN,
677                0x1 << VehicleAudioStream.STREAM0,
678                0);
679
680        // now music release focus.
681        mAudioManager.abandonAudioFocus(listenerMusic);
682        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
683        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
684        assertEquals(0, request[1]);
685        assertEquals(0, request[2]);
686        assertEquals(0, request[3]);
687        assertArrayEquals(new int[] {0, 0, 0, 0},
688                mExtRoutingHintPropertyHandler.getLastHint());
689        mAudioFocusPropertyHandler.sendAudioFocusState(
690                VehicleAudioFocusState.STATE_LOSS,
691                0,
692                VehicleAudioExtFocusFlag.NONE_FLAG);
693    }
694
695    private void checkSingleRequestRelease(AudioAttributes attrb, int androidFocusToRequest,
696            int[] expectedExtRouting, int expectedStreams,
697            int expectedExtState, int expectedContexts) throws Exception {
698        AudioFocusListener lister = new AudioFocusListener();
699        int res = mCarAudioManager.requestAudioFocus(lister, attrb, androidFocusToRequest, 0);
700        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
701        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
702        int expectedFocusRequest = VehicleAudioFocusRequest.REQUEST_RELEASE;
703        int response = VehicleAudioFocusState.STATE_LOSS;
704        switch (androidFocusToRequest) {
705            case AudioManager.AUDIOFOCUS_GAIN:
706                expectedFocusRequest = VehicleAudioFocusRequest.REQUEST_GAIN;
707                response = VehicleAudioFocusState.STATE_GAIN;
708                break;
709            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
710                expectedFocusRequest =
711                    VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT;
712                response = VehicleAudioFocusState.STATE_GAIN_TRANSIENT;
713                break;
714            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
715                expectedFocusRequest =
716                    VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK;
717                response = VehicleAudioFocusState.STATE_GAIN_TRANSIENT;
718                break;
719        }
720        assertEquals(expectedFocusRequest, request[0]);
721        assertEquals(expectedStreams, request[1]);
722        assertEquals(expectedExtState, request[2]);
723        assertEquals(expectedContexts, request[3]);
724        assertArrayEquals(expectedExtRouting, mExtRoutingHintPropertyHandler.getLastHint());
725        mAudioFocusPropertyHandler.sendAudioFocusState(
726                response,
727                request[1],
728                VehicleAudioExtFocusFlag.NONE_FLAG);
729        mAudioManager.abandonAudioFocus(lister);
730        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
731        assertEquals(VehicleAudioFocusRequest.REQUEST_RELEASE, request[0]);
732        assertEquals(0, request[1]);
733        assertEquals(0, request[2]);
734        assertEquals(0, request[3]);
735        assertArrayEquals(new int[] {0, 0, 0, 0},
736                mExtRoutingHintPropertyHandler.getLastHint());
737        mAudioFocusPropertyHandler.sendAudioFocusState(
738                VehicleAudioFocusState.STATE_LOSS,
739                request[1],
740                VehicleAudioExtFocusFlag.NONE_FLAG);
741    }
742
743    public void testRadioMute() throws Exception {
744        testMediaMute(CarAudioManager.CAR_AUDIO_USAGE_RADIO,
745                0,
746                VehicleAudioExtFocusFlag.PLAY_ONLY_FLAG,
747                VehicleAudioContextFlag.RADIO_FLAG);
748    }
749
750    public void testMusicMute() throws Exception {
751        testMediaMute(CarAudioManager.CAR_AUDIO_USAGE_MUSIC,
752                0x1,
753                0,
754                VehicleAudioContextFlag.MUSIC_FLAG);
755    }
756
757    private void testMediaMute(int mediaUsage, int primaryStream, int extFocusFlag,
758            int mediaContext) throws Exception {
759        // android radio
760        AudioFocusListener listenerMedia = new AudioFocusListener();
761        CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager(
762                Car.AUDIO_SERVICE);
763        assertNotNull(carAudioManager);
764        AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(mediaUsage);
765        Log.i(TAG, "request media Focus");
766        int res = doRequestFocus(mAudioManager, listenerMedia,
767                radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
768        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
769        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
770        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
771        assertEquals(primaryStream, request[1]);
772        assertEquals(extFocusFlag, request[2]);
773        assertEquals(mediaContext, request[3]);
774        if (mediaUsage == CarAudioManager.CAR_AUDIO_USAGE_RADIO) {
775            assertArrayEquals(new int[] {1, 0, 0, 0},
776                    mExtRoutingHintPropertyHandler.getLastHint());
777        } else {
778            assertArrayEquals(new int[] {0, 0, 0, 0},
779                    mExtRoutingHintPropertyHandler.getLastHint());
780        }
781        mAudioFocusPropertyHandler.sendAudioFocusState(
782                VehicleAudioFocusState.STATE_GAIN,
783                primaryStream,
784                extFocusFlag);
785        // now mute it.
786        assertFalse(carAudioManager.isMediaMuted());
787        Log.i(TAG, "mute media");
788        assertTrue(carAudioManager.setMediaMute(true));
789        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
790        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT,
791                request[0]);
792        assertEquals(0, request[1]);
793        assertEquals(VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG,
794                request[2]);
795        assertEquals(0, request[3]);
796        assertArrayEquals(new int[] {0, 0, 0, 0},
797                mExtRoutingHintPropertyHandler.getLastHint());
798        mAudioFocusPropertyHandler.sendAudioFocusState(
799                VehicleAudioFocusState.STATE_GAIN,
800                0,
801                VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG);
802        assertTrue(carAudioManager.isMediaMuted());
803        // nav guidance on top of it
804        AudioFocusListener listenerNav = new AudioFocusListener();
805        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
806                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
807                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
808                build();
809        Log.i(TAG, "request nav Focus");
810        res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
811                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
812        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
813        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
814                request[0]);
815        assertEquals(0x1 << VehicleAudioStream.STREAM1, request[1]);
816        assertEquals(VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG,
817                request[2]);
818        assertEquals(VehicleAudioContextFlag.NAVIGATION_FLAG, request[3]);
819        assertArrayEquals(new int[] {0, 0, 0, 0},
820                mExtRoutingHintPropertyHandler.getLastHint());
821        assertTrue(carAudioManager.isMediaMuted());
822        mAudioFocusPropertyHandler.sendAudioFocusState(
823                VehicleAudioFocusState.STATE_GAIN,
824                0x1 << VehicleAudioStream.STREAM1,
825                VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG);
826        assertTrue(carAudioManager.isMediaMuted());
827        // nav guidance ends
828        mAudioManager.abandonAudioFocus(listenerNav);
829        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
830        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT,
831                request[0]);
832        assertEquals(0, request[1]);
833        assertEquals(VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG,
834                request[2]);
835        assertEquals(0, request[3]);
836        assertArrayEquals(new int[] {0, 0, 0, 0},
837                mExtRoutingHintPropertyHandler.getLastHint());
838        assertTrue(carAudioManager.isMediaMuted());
839        mAudioFocusPropertyHandler.sendAudioFocusState(
840                VehicleAudioFocusState.STATE_GAIN,
841                0,
842                VehicleAudioExtFocusFlag.MUTE_MEDIA_FLAG);
843        // now unmute it. media should resume.
844        assertTrue(carAudioManager.isMediaMuted());
845        assertFalse(carAudioManager.setMediaMute(false));
846        assertFalse(carAudioManager.isMediaMuted());
847        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
848        assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
849        assertEquals(primaryStream, request[1]);
850        assertEquals(extFocusFlag,
851                request[2]);
852        assertEquals(mediaContext, request[3]);
853        if (mediaUsage == CarAudioManager.CAR_AUDIO_USAGE_RADIO) {
854            assertArrayEquals(new int[] {1, 0, 0, 0},
855                    mExtRoutingHintPropertyHandler.getLastHint());
856        } else {
857            assertArrayEquals(new int[] {0, 0, 0, 0},
858                    mExtRoutingHintPropertyHandler.getLastHint());
859        }
860        assertFalse(carAudioManager.isMediaMuted());
861        mAudioFocusPropertyHandler.sendAudioFocusState(
862                VehicleAudioFocusState.STATE_GAIN,
863                primaryStream,
864                extFocusFlag);
865        assertFalse(carAudioManager.isMediaMuted());
866        // release focus
867        mAudioManager.abandonAudioFocus(listenerMedia);
868    }
869
870    protected static class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
871        private final Semaphore mFocusChangeWait = new Semaphore(0);
872        private int mLastFocusChange;
873
874        public int waitAndGetFocusChange(long timeoutMs) throws Exception {
875            if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
876                fail("timeout waiting for focus change");
877            }
878            return mLastFocusChange;
879        }
880
881        public void waitForFocus(long timeoutMs, int expectedFocus) throws Exception {
882            while (mLastFocusChange != expectedFocus) {
883                if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
884                    fail("timeout waiting for focus change");
885                }
886            }
887        }
888
889        @Override
890        public void onAudioFocusChange(int focusChange) {
891            mLastFocusChange = focusChange;
892            mFocusChangeWait.release();
893        }
894    }
895
896    protected static class FocusPropertyHandler implements VehicleHalPropertyHandler {
897
898        private int mState = VehicleAudioFocusState.STATE_LOSS;
899        private int mStreams = 0;
900        private int mExtFocus = 0;
901        private int mRequest;
902        private int mRequestedStreams;
903        private int mRequestedExtFocus;
904        private int mRequestedAudioContexts;
905        private final MockedCarTestBase mCarTest;
906
907        private final Semaphore mSetWaitSemaphore = new Semaphore(0);
908
909        public FocusPropertyHandler(MockedCarTestBase carTest) {
910            mCarTest = carTest;
911        }
912
913        public void sendAudioFocusState(int state, int streams, int extFocus) {
914            synchronized (this) {
915                mState = state;
916                mStreams = streams;
917                mExtFocus = extFocus;
918            }
919            mCarTest.getMockedVehicleHal().injectEvent(
920                    VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_FOCUS)
921                            .setTimestamp(SystemClock.elapsedRealtimeNanos())
922                            .addIntValue(state, streams, extFocus, 0)
923                            .build());
924        }
925
926        public int[] waitForAudioFocusRequest(long timeoutMs) throws Exception {
927            if (!mSetWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
928                fail("timeout");
929            }
930            synchronized (this) {
931                return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus,
932                        mRequestedAudioContexts };
933            }
934        }
935
936        @Override
937        public void onPropertySet(VehiclePropValue value) {
938            Log.i(TAG, "onPropertySet, prop: 0x" + toHexString(value.prop));
939            assertEquals(AUDIO_FOCUS, value.prop);
940            ArrayList<Integer> v = value.value.int32Values;
941            synchronized (this) {
942                mRequest = v.get(VehicleAudioFocusIndex.FOCUS);
943                mRequestedStreams = v.get(VehicleAudioFocusIndex.STREAMS);
944                mRequestedExtFocus = v.get(VehicleAudioFocusIndex.EXTERNAL_FOCUS_STATE);
945                mRequestedAudioContexts = v.get(VehicleAudioFocusIndex.AUDIO_CONTEXTS);
946            }
947            Log.i(TAG, "onPropertySet, values: " + Arrays.toString(v.toArray()));
948            mSetWaitSemaphore.release();
949        }
950
951        @Override
952        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
953            assertEquals(AUDIO_FOCUS, value.prop);
954            int state, streams, extFocus;
955            synchronized (this) {
956                state = mState;
957                streams = mStreams;
958                extFocus = mExtFocus;
959            }
960            return VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_FOCUS)
961                    .setTimestamp(SystemClock.elapsedRealtimeNanos())
962                    .addIntValue(state, streams, extFocus, 0)
963                    .build();
964        }
965
966        @Override
967        public void onPropertySubscribe(int property, int zones, float sampleRate) {
968            assertEquals(AUDIO_FOCUS, property);
969        }
970
971        @Override
972        public void onPropertyUnsubscribe(int property) {
973            assertEquals(AUDIO_FOCUS, property);
974        }
975    }
976
977    private static class ExtRoutingHintPropertyHandler extends FailingPropertyHandler {
978        private int[] mLastHint = {0, 0, 0, 0};
979
980        public int[] getLastHint() {
981            int[] lastHint = new int[mLastHint.length];
982            synchronized (this) {
983                System.arraycopy(mLastHint, 0, lastHint, 0, mLastHint.length);
984            }
985            return lastHint;
986        }
987
988        @Override
989        public void onPropertySet(VehiclePropValue value) {
990            assertEquals(VehicleProperty.AUDIO_EXT_ROUTING_HINT, value.prop);
991            assertEquals(mLastHint.length, value.value.int32Values.size());
992            synchronized (this) {
993                for (int i = 0; i < mLastHint.length; i++) {
994                    mLastHint[i] = value.value.int32Values.get(i);
995                }
996            }
997        }
998    }
999}
1000