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